mirror of
https://github.com/kennethreitz/heroku-buildpack-python.git
synced 2026-06-05 23:10:16 +00:00
Compare commits
214 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ddde30a12e | |||
| 2db5578998 | |||
| 290250836f | |||
| 0ff4df4dc3 | |||
| 9177aa2c08 | |||
| ad791dc7f8 | |||
| 6b298ff7f3 | |||
| 01e30e938c | |||
| e81632e05e | |||
| 4ab3358851 | |||
| 4ded988907 | |||
| 8637dc05c0 | |||
| 6f65ad0b97 | |||
| c5ec378ea7 | |||
| 37013c2eca | |||
| 54a8a6407e | |||
| 69360fb804 | |||
| 92d296ec24 | |||
| 99557332a0 | |||
| a3d9c5a1b2 | |||
| 235ff64a38 | |||
| 0f7fca9e03 | |||
| 161bf49a3a | |||
| 7ac90f19dd | |||
| 90cc426c82 | |||
| 550a239553 | |||
| 57fea1bd6f | |||
| d758545164 | |||
| 5935632b8b | |||
| 8f3a7b7947 | |||
| 2f47bd12fd | |||
| 86fb607dd4 | |||
| 80823b1e16 | |||
| ff6f56b8c5 | |||
| 5393ba3cb4 | |||
| 4ade6c0457 | |||
| e5c389e677 | |||
| ea27b5a77c | |||
| 18c19e4466 | |||
| 0ea950ee52 | |||
| 2d3033fc71 | |||
| 1687f1cffe | |||
| 2f0263a5b8 | |||
| dfbf20acd2 | |||
| 0b9019ab71 | |||
| b0fc2245f4 | |||
| 113c669041 | |||
| b8b5e12a4a | |||
| bece3de495 | |||
| 89120f7797 | |||
| 99e035495b | |||
| 0c56b0c35c | |||
| 111f92e368 | |||
| 03e666ffea | |||
| 40598be9d4 | |||
| 8f057f3c9b | |||
| 972e6f8054 | |||
| aeb7ef392a | |||
| 930a63d55c | |||
| 544de727a0 | |||
| 466e6b5fe3 | |||
| 1abaaf6cf1 | |||
| 48bd910207 | |||
| 87c580f819 | |||
| db1df1ac67 | |||
| 4005d7ea50 | |||
| d29d4404c3 | |||
| 33779576bd | |||
| 3bddd66637 | |||
| e00a407c91 | |||
| 117dcff4e1 | |||
| 7243afd5b5 | |||
| 0a75bc9744 | |||
| ca3b3808b9 | |||
| 287b398826 | |||
| 76d2eda80e | |||
| 880c498c3f | |||
| 12f053ef7f | |||
| 35ee2f60bd | |||
| 3e480e9aea | |||
| 46ba98a4ee | |||
| 958de9441a | |||
| 74312f5487 | |||
| ecf5278679 | |||
| 863abfb00e | |||
| f782693537 | |||
| a2c2411109 | |||
| 712f02e359 | |||
| 61477e515a | |||
| be9343534a | |||
| 2b4e9be4ad | |||
| 382dff7e3b | |||
| 6f2012c1c9 | |||
| c71fe81f8d | |||
| 91586d6690 | |||
| 046726b864 | |||
| ddf353cff5 | |||
| 160497cb64 | |||
| 6194f94aa7 | |||
| e288332f0e | |||
| e4979f33e3 | |||
| 03c3aebca8 | |||
| 487a5ffb95 | |||
| 01d15d4d0e | |||
| 6655794b4d | |||
| 4beb57a5a0 | |||
| bac517e755 | |||
| 62b68cfcd9 | |||
| 481e800a7c | |||
| 9a7c6c339d | |||
| 23340bcfee | |||
| 102979e985 | |||
| ab9a4fbe5f | |||
| aad72f8e81 | |||
| 72295bf186 | |||
| c45a2c14b2 | |||
| ee177b28ff | |||
| 2e0f2f59a0 | |||
| 9856efa7c4 | |||
| 34878b9638 | |||
| 27abdfe7d7 | |||
| 3b0bdba799 | |||
| b547d8c30c | |||
| 1bd317cd50 | |||
| 60b57fd368 | |||
| 27a22e8ccb | |||
| 0378bbb491 | |||
| bccec5cee9 | |||
| d6fbb29977 | |||
| 9d0bf98a52 | |||
| 795272d625 | |||
| c3866f80b6 | |||
| 580a662f36 | |||
| 5767e0df04 | |||
| eeb63f215a | |||
| 5a6e4e49b4 | |||
| b6eada4e67 | |||
| bbe0acf001 | |||
| 05e65801fa | |||
| 08338a4a26 | |||
| bf913e0016 | |||
| 774ceb3367 | |||
| 335a14c714 | |||
| 4dea56d4a0 | |||
| c9916dd2e3 | |||
| 662a311f95 | |||
| bfd6ea22ca | |||
| c5d7991468 | |||
| c4404694e1 | |||
| e914736956 | |||
| 23833dda9b | |||
| 15573b9d3f | |||
| 86661c1cda | |||
| 9cbace7f76 | |||
| 74cb870488 | |||
| ae9b83f07c | |||
| d70c681c32 | |||
| c312d917a1 | |||
| 5d2ecd2f1f | |||
| a1e1dbd865 | |||
| 12f28894ce | |||
| 538ef6e378 | |||
| 1588ea44b2 | |||
| 3cc9098e25 | |||
| 436e945a91 | |||
| f9b83a4e95 | |||
| b1f8bf9f5e | |||
| 7af5175942 | |||
| c24a9096de | |||
| 36d27c46ed | |||
| 713fa36bca | |||
| 20249f9672 | |||
| 8b55e73f77 | |||
| 2058b3c5bf | |||
| 4eb17eb9a9 | |||
| 7138b7afb2 | |||
| fe9b9401d3 | |||
| 8c870cabfb | |||
| b8182c077a | |||
| fbbdadd347 | |||
| b54faf36f5 | |||
| a0dc2a6f33 | |||
| f775fca31c | |||
| efebff4ce8 | |||
| 94b2ab43a5 | |||
| a21dbc2da1 | |||
| cf5a9d26d1 | |||
| ef4772b87a | |||
| ffda318476 | |||
| 6aff0d0197 | |||
| b9ebd33703 | |||
| 61e17df22d | |||
| b88d0ad800 | |||
| 7e24f94164 | |||
| 3a239aa71a | |||
| 0e1ae7a798 | |||
| 133c308796 | |||
| 7ea8c861a2 | |||
| 20e0af2bf4 | |||
| 5d0ed4e7cd | |||
| 144629d7dc | |||
| cf36e8c7cb | |||
| 233df5d59d | |||
| 375796063b | |||
| d8a70de8a8 | |||
| f1ee7fc04f | |||
| e8585fb4a7 | |||
| e10cc01f7e | |||
| 79d6db74b1 | |||
| 69d607a76e | |||
| 2c03d89213 | |||
| 11bf9833da | |||
| d2d37255d5 | |||
| bea5b4919e |
@@ -0,0 +1,6 @@
|
|||||||
|
language: python
|
||||||
|
python:
|
||||||
|
- 2.7
|
||||||
|
script: make tests
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
@@ -1,3 +1,71 @@
|
|||||||
|
## v14
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
* Full removal of Django settings injection for new apps.
|
||||||
|
* Support for profile.d
|
||||||
|
* Fresh app detection.
|
||||||
|
* Update to Virtualenv v1.7.2
|
||||||
|
* Updated to Pip v1.1 (patched)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Default pip path exists action.
|
||||||
|
|
||||||
|
## v13
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Fix pip quoting error.
|
||||||
|
* Only talk about collectstatic in buildpack output when it's configured.
|
||||||
|
|
||||||
|
## v12
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Catch database setting corner case.
|
||||||
|
|
||||||
|
## v11
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Cleanup collectstatic output.
|
||||||
|
|
||||||
|
|
||||||
|
## v10
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Check for collectstatic validity with --dry-run instead of --help for Django 1.4.
|
||||||
|
|
||||||
|
## v9
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Unset PYTHONHOME in buildpack for [user_env_compile](http://devcenter.heroku.com/articles/labs-user-env-compile).
|
||||||
|
|
||||||
|
## v8
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
* Disable Django collectstatic with `$DISABLE_COLLECTSTATIC` + [user_env_compile](http://devcenter.heroku.com/articles/labs-user-env-compile).
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Don't disbable injection for new Django apps.
|
||||||
|
* Inform user of July 1, 2012 deprecation of Django injection.
|
||||||
|
|
||||||
|
## v7
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
* Full removal of Django setting injection for new apps.
|
||||||
|
* Automatic execution of collectstatic.
|
||||||
|
* Suppress collectstatic errors via env SILENCE_COLLECTSTATIC.
|
||||||
|
* Increase settings.py search depth to 3.
|
||||||
|
* Search recursively from included requirements.txt files.
|
||||||
|
|
||||||
|
|
||||||
## v6 (03/23/2012)
|
## v6 (03/23/2012)
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License:
|
MIT License:
|
||||||
|
|
||||||
Copyright (C) 2012 Heroku, Inc.
|
Copyright (C) 2013 Heroku, Inc.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,17 @@ docs:
|
|||||||
mkdir -p site
|
mkdir -p site
|
||||||
shocco -t 'Python Buildpack Compiler' ./bin/compile > site/index.html
|
shocco -t 'Python Buildpack Compiler' ./bin/compile > site/index.html
|
||||||
shocco -t 'Django Buildpack Compiler' ./bin/steps/django > site/django.html
|
shocco -t 'Django Buildpack Compiler' ./bin/steps/django > site/django.html
|
||||||
|
shocco -t 'Python Buildpack Detector' ./bin/detect > site/detect.html
|
||||||
shocco -t 'Pylibmc Buildpack Compiler' ./bin/steps/pylibmc > site/pylibmc.html
|
shocco -t 'Pylibmc Buildpack Compiler' ./bin/steps/pylibmc > site/pylibmc.html
|
||||||
|
shocco -t 'Python Buildpack Changelog' ./Changelog.md > site/changelog.html
|
||||||
|
|
||||||
site: docs
|
site: docs
|
||||||
cd site && git add -A && git commit -m 'update' && git push heroku master
|
cd site && git add -A && git commit -m 'update' && git push heroku master
|
||||||
|
|
||||||
pip:
|
pip:
|
||||||
git clone git@github.com:kennethreitz/pip.git --branch heroku --depth 1
|
git clone git@github.com:kennethreitz/pip.git --branch heroku --depth 1
|
||||||
rm -fr vendor/virtualenv-1.7/virtualenv_support/pip-1.0.2.tar.gz
|
rm -fr vendor/virtualenv-1.8.4/virtualenv_support/pip-1.2.1.tar.gz
|
||||||
rm -fr pip/.git
|
rm -fr pip/.git
|
||||||
tar -pczf vendor/virtualenv-1.7/virtualenv_support/pip-1.0.2.tar.gz pip
|
tar -pczf vendor/virtualenv-1.8.4/virtualenv_support/pip-1.2.1.tar.gz pip
|
||||||
rm -fr pip
|
rm -fr pip
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
Heroku buildpack: Python
|
Heroku buildpack: Python
|
||||||
========================
|
========================
|
||||||
|
|
||||||
This is a [Heroku buildpack](http://devcenter.heroku.com/articles/buildpacks) for Python apps.
|
This is a [Heroku buildpack](http://devcenter.heroku.com/articles/buildpacks) for Python apps, powered by [pip](http://www.pip-installer.org/).
|
||||||
It uses [virtualenv](http://www.virtualenv.org/) and [pip](http://www.pip-installer.org/).
|
|
||||||
|
[](http://travis-ci.org/heroku/heroku-buildpack-python)
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
@@ -12,18 +13,17 @@ Example usage:
|
|||||||
$ ls
|
$ ls
|
||||||
Procfile requirements.txt web.py
|
Procfile requirements.txt web.py
|
||||||
|
|
||||||
$ heroku create --stack cedar --buildpack git@github.com:heroku/heroku-buildpack-python.git
|
$ heroku create --stack cedar --buildpack git://github.com/heroku/heroku-buildpack-python.git
|
||||||
|
|
||||||
$ git push heroku master
|
$ git push heroku master
|
||||||
...
|
...
|
||||||
-----> Heroku receiving push
|
-----> Fetching custom git buildpack... done
|
||||||
-----> Fetching custom build pack... done
|
|
||||||
-----> Python app detected
|
-----> Python app detected
|
||||||
-----> Preparing virtualenv version 1.6.4
|
-----> No runtime.txt provided; assuming python-2.7.3.
|
||||||
New python executable in ./bin/python
|
-----> Preparing Python runtime (python-2.7.3)
|
||||||
Installing setuptools............done.
|
-----> Installing Distribute (0.6.34)
|
||||||
Installing pip...............done.
|
-----> Installing Pip (1.2.1)
|
||||||
-----> Installing dependencies using pip version 1.0.2
|
-----> Installing dependencies using Pip (1.2.1)
|
||||||
Downloading/unpacking Flask==0.7.2 (from -r requirements.txt (line 1))
|
Downloading/unpacking Flask==0.7.2 (from -r requirements.txt (line 1))
|
||||||
Downloading/unpacking Werkzeug>=0.6.1 (from Flask==0.7.2->-r requirements.txt (line 1))
|
Downloading/unpacking Werkzeug>=0.6.1 (from Flask==0.7.2->-r requirements.txt (line 1))
|
||||||
Downloading/unpacking Jinja2>=2.4 (from Flask==0.7.2->-r requirements.txt (line 1))
|
Downloading/unpacking Jinja2>=2.4 (from Flask==0.7.2->-r requirements.txt (line 1))
|
||||||
@@ -31,13 +31,24 @@ Example usage:
|
|||||||
Successfully installed Flask Werkzeug Jinja2
|
Successfully installed Flask Werkzeug Jinja2
|
||||||
Cleaning up...
|
Cleaning up...
|
||||||
|
|
||||||
The buildpack will detect your app as Python if it has the file `requirements.txt` in the root. It will detect your app as Python/Django if there is an additional `settings.py` in a project subdirectory.
|
You can also add it to upcoming builds of an existing application:
|
||||||
|
|
||||||
It will use virtualenv and pip to install your dependencies, vendoring a copy of the Python runtime into your slug. The `bin/`, `include/` and `lib/` directories will be cached between builds to allow for faster pip install time.
|
$ heroku config:add BUILDPACK_URL=git://github.com/heroku/heroku-buildpack-python.git
|
||||||
|
|
||||||
Hacking
|
The buildpack will detect your app as Python if it has the file `requirements.txt` in the root.
|
||||||
-------
|
|
||||||
|
|
||||||
To use this buildpack, fork it on Github. Push up changes to your fork, then create a test app with `--buildpack <your-github-url>` and push to it.
|
It will use Pip to install your dependencies, vendoring a copy of the Python runtime into your slug.
|
||||||
|
|
||||||
To change the vendored virtualenv, unpack the desired version to the `src/` folder, and update the virtualenv() function in `bin/compile` to prepend the virtualenv module directory to the path. The virtualenv release vendors its own versions of pip and setuptools.
|
Specify a Runtime
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
You can also provide arbitrary releases Python with a `runtime.txt` file.
|
||||||
|
|
||||||
|
$ cat runtime.txt
|
||||||
|
python-3.3.0
|
||||||
|
|
||||||
|
Runtime options include:
|
||||||
|
|
||||||
|
- python-2.7.3
|
||||||
|
- python-3.3.0
|
||||||
|
- pypy-1.9 (experimental)
|
||||||
|
|||||||
+106
-123
@@ -1,30 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# This script serves as the
|
# Usage:
|
||||||
# [**Python Buildpack**](https://github.com/heroku/heroku-buildpack-python)
|
|
||||||
# compiler.
|
|
||||||
#
|
|
||||||
# A [buildpack](http://devcenter.heroku.com/articles/buildpacks) is an
|
|
||||||
# adapter between a Python application and Heroku's runtime.
|
|
||||||
|
|
||||||
# ## Usage
|
|
||||||
# Compiling an app into a slug is simple:
|
|
||||||
#
|
#
|
||||||
# $ bin/compile <build-dir> <cache-dir>
|
# $ bin/compile <build-dir> <cache-dir>
|
||||||
|
|
||||||
|
|
||||||
# ## Assumptions
|
|
||||||
#
|
|
||||||
# This buildpack makes the following assumptions:
|
|
||||||
#
|
|
||||||
# - The desired Python VM is available on the base system.
|
|
||||||
# - Library dependencies are available on the base system.
|
|
||||||
# - Django applications should not require any platform-specific configuration.
|
|
||||||
|
|
||||||
# <hr />
|
|
||||||
|
|
||||||
# ## Context
|
|
||||||
|
|
||||||
# Fail fast and fail hard.
|
# Fail fast and fail hard.
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
@@ -37,58 +17,53 @@ ROOT_DIR=$(dirname $BIN_DIR)
|
|||||||
BUILD_DIR=$1
|
BUILD_DIR=$1
|
||||||
CACHE_DIR=$2
|
CACHE_DIR=$2
|
||||||
|
|
||||||
# The detected application type (`Python`|`Python/Django`).
|
|
||||||
NAME=$($BIN_DIR/detect $BUILD_DIR)
|
|
||||||
|
|
||||||
# Where to store the Pip download cache.
|
|
||||||
CACHED_DIRS=".heroku"
|
CACHED_DIRS=".heroku"
|
||||||
PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE:-$CACHE_DIR/pip_downloads}
|
|
||||||
|
|
||||||
# Static configurations for virtualenv caches.
|
# Static configurations for virtualenv caches.
|
||||||
LEGACY_VIRTUALENV_LOC="."
|
VIRTUALENV_LOC=".heroku/venv"
|
||||||
MODERN_VIRTUALENV_LOC=".heroku/venv"
|
LEGACY_TRIGGER="lib/python2.7"
|
||||||
LEGACY_VIRTUALENV_DIRS="bin include lib"
|
PROFILE_PATH="$BUILD_DIR/.profile.d/python.sh"
|
||||||
LEGACY_VIRTUALENV_TRIGGER="lib/python2.7"
|
|
||||||
|
|
||||||
|
|
||||||
# Python version. This will be used in the future to specify custom Pythons.
|
# Python version. This will be used in the future to specify custom Pythons.
|
||||||
PYTHON_VERSION="2.7.2"
|
DEFAULT_PYTHON_VERSION="python-2.7.3"
|
||||||
PYTHON_EXE="python2.7"
|
PYTHON_EXE="/app/.heroku/python/bin/python"
|
||||||
|
|
||||||
# The slug compiler doesn't do a very good job of sanitizing environment variables.
|
# Sanitizing environment variables.
|
||||||
unset GIT_DIR
|
unset GIT_DIR PYTHONHOME PYTHONPATH LD_LIBRARY_PATH LIBRARY_PATH
|
||||||
|
|
||||||
# We'll need to send these statics to other scripts we `source`.
|
# We'll need to send these statics to other scripts we `source`.
|
||||||
export PIP_DOWNLOAD_CACHE
|
export BUILD_DIR CACHE_DIR BIN_DIR PROFILE_PATH
|
||||||
export BUILD_DIR
|
|
||||||
|
|
||||||
# Syntax sugar.
|
# Syntax sugar.
|
||||||
indent() {
|
source $BIN_DIR/utils
|
||||||
RE="s/^/ /"
|
|
||||||
[ $(uname) == "Darwin" ] && sed -l "$RE" || sed -u "$RE"
|
|
||||||
}
|
|
||||||
|
|
||||||
function virtualenv (){
|
# Directory Hacks for path consistiency.
|
||||||
python "$ROOT_DIR/vendor/virtualenv-1.7/virtualenv.py" "$@"
|
APP_DIR='/app'
|
||||||
}
|
TMP_APP_DIR=$CACHE_DIR/tmp_app_dir
|
||||||
|
|
||||||
function puts-step (){
|
# Copy Anvil app dir to temporary storage...
|
||||||
echo "-----> $@"
|
mkdir -p $TMP_APP_DIR
|
||||||
}
|
deep-mv $APP_DIR $TMP_APP_DIR
|
||||||
|
|
||||||
function puts-warn (){
|
# Copy Application code in.
|
||||||
echo " ! $@"
|
deep-mv $BUILD_DIR $APP_DIR
|
||||||
}
|
|
||||||
|
|
||||||
# ## Build Time
|
# Set new context.
|
||||||
#
|
ORIG_BUILD_DIR=$BUILD_DIR
|
||||||
|
BUILD_DIR=$APP_DIR
|
||||||
|
|
||||||
|
# Prepend proper path buildpack use.
|
||||||
|
export PATH=$BUILD_DIR/.heroku/python/bin:$PATH
|
||||||
|
export PYTHONUNBUFFERED=1
|
||||||
|
export LANG=en_US.UTF-8
|
||||||
|
export LIBRARY_PATH=/app/.heroku/vendor/lib
|
||||||
|
export LD_LIBRARY_PATH=/app/.heroku/vendor/lib
|
||||||
|
|
||||||
# Switch to the repo's context.
|
# Switch to the repo's context.
|
||||||
cd $BUILD_DIR
|
cd $BUILD_DIR
|
||||||
|
|
||||||
# ### Sanity Checks
|
# Experimental pre_compile hook.
|
||||||
#
|
source $BIN_DIR/steps/hooks/pre_compile
|
||||||
# Just a little peace of mind.
|
|
||||||
|
|
||||||
# If no requirements given, assume `setup.py develop`.
|
# If no requirements given, assume `setup.py develop`.
|
||||||
if [ ! -f requirements.txt ]; then
|
if [ ! -f requirements.txt ]; then
|
||||||
@@ -96,62 +71,74 @@ if [ ! -f requirements.txt ]; then
|
|||||||
echo "-e ." > requirements.txt
|
echo "-e ." > requirements.txt
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# If no runtime given, assume default version.
|
||||||
|
if [ ! -f runtime.txt ]; then
|
||||||
|
puts-step "No runtime.txt provided; assuming $DEFAULT_PYTHON_VERSION."
|
||||||
|
echo $DEFAULT_PYTHON_VERSION > runtime.txt
|
||||||
|
fi
|
||||||
|
|
||||||
# ### The Cache
|
# ### The Cache
|
||||||
mkdir -p $CACHE_DIR
|
mkdir -p $CACHE_DIR
|
||||||
|
|
||||||
# Create set-aside `.heroku` folder.
|
# Purge "old-style" virtualenvs.
|
||||||
mkdir -p .heroku
|
[ -d $CACHE_DIR/$LEGACY_TRIGGER ] && rm -fr $CACHE_DIR/.heroku/bin $CACHE_DIR/.heroku/lib $CACHE_DIR/.heroku/include
|
||||||
|
[ -d $CACHE_DIR/$VIRTUALENV_LOC ] && rm -fr $CACHE_DIR/.heroku/venv $CACHE_DIR/.heroku/src
|
||||||
# Nice defaults.
|
|
||||||
LEGACY_VIRTUALENV=false
|
|
||||||
VIRTUALENV_LOC=$MODERN_VIRTUALENV_LOC
|
|
||||||
|
|
||||||
# Support "old-style" virtualenvs.
|
|
||||||
if [ -d $CACHE_DIR/$LEGACY_VIRTUALENV_TRIGGER ]; then
|
|
||||||
LEGACY_VIRTUALENV=true
|
|
||||||
VIRTUALENV_LOC=$LEGACY_VIRTUALENV_LOC
|
|
||||||
CACHED_DIRS=$LEGACY_VIRTUALENV_DIRS
|
|
||||||
|
|
||||||
# Warn for a checked-in virtualenv.
|
|
||||||
if [ -d "lib" ] || [ -d "bin" ]; then
|
|
||||||
puts-warn "You have a virtualenv checked in. You should ignore the appropriate paths in your repo. See http://devcenter.heroku.com/articles/gitignore for more info.";
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Reject a conflicting checked-in virtualenv.
|
|
||||||
if [ -f "lib/python2.7" ]; then
|
|
||||||
puts-warn "Checked-in virtualenv conflict."
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Restore old artifacts from the cache.
|
# Restore old artifacts from the cache.
|
||||||
for dir in $CACHED_DIRS; do
|
for dir in $CACHED_DIRS; do
|
||||||
cp -R $CACHE_DIR/$dir . &> /dev/null || true
|
cp -R $CACHE_DIR/$dir . &> /dev/null || true
|
||||||
done
|
done
|
||||||
|
|
||||||
|
set +e
|
||||||
|
# Create set-aside `.heroku` folder.
|
||||||
|
mkdir .heroku &> /dev/null
|
||||||
|
set -e
|
||||||
|
|
||||||
# ### Virtualenv Setup
|
mkdir -p $(dirname $PROFILE_PATH)
|
||||||
#
|
|
||||||
|
|
||||||
# Create the virtualenv. Rebuild if corrupt.
|
|
||||||
# TODO: Bootstrap a bottled Python VM...
|
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
puts-step "Preparing Python interpreter ($PYTHON_VERSION)"
|
PYTHON_VERSION=$(cat runtime.txt)
|
||||||
puts-step "Creating Virtualenv version $(virtualenv --version)"
|
|
||||||
|
|
||||||
# Try to create the virtualenv.
|
# Install Python.
|
||||||
OUT=$(virtualenv --python $PYTHON_EXE --distribute --never-download --prompt='(venv) ' $VIRTUALENV_LOC 2>&1)
|
if [ -f .heroku/python-version ]; then
|
||||||
|
if [ ! $(cat .heroku/python-version) = $PYTHON_VERSION ]; then
|
||||||
|
puts-step "Found $(cat .heroku/python-version), removing."
|
||||||
|
rm -fr .heroku/python
|
||||||
|
else
|
||||||
|
SKIP_INSTALL=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# If there's an error, purge and recreate.
|
|
||||||
[ $? -ne 0 ] && {
|
if [ ! "$SKIP_INSTALL" ]; then
|
||||||
puts-warn "Virtualenv corrupt, rebuilding."
|
puts-step "Preparing Python runtime ($PYTHON_VERSION)"
|
||||||
for dir in $VIRTUALENV_DIRS; do
|
curl http://envy-versions.s3.amazonaws.com/$PYTHON_VERSION.tar.bz2 -s | tar jx &> /dev/null
|
||||||
rm -fr $dir &> /dev/null || true
|
if [[ $? != 0 ]] ; then
|
||||||
done
|
puts-warn "Requested runtime ($PYTHON_VERSION) was not found."
|
||||||
OUT=$(virtualenv --python $PYTHON_EXE --distribute --never-download --prompt='(venv) ' $VIRTUALENV_LOC )
|
puts-warn "Aborting. More info: https://devcenter.heroku.com/articles/python-support"
|
||||||
}
|
exit 1
|
||||||
echo "$OUT" | indent
|
fi
|
||||||
|
mv python .heroku/python
|
||||||
|
|
||||||
|
# Record for future reference.
|
||||||
|
echo $PYTHON_VERSION > .heroku/python-version
|
||||||
|
WORKING_DIR=$(pwd)
|
||||||
|
|
||||||
|
# Prepare it for the real world
|
||||||
|
puts-step "Installing Distribute (0.6.34)"
|
||||||
|
cd $ROOT_DIR/vendor/distribute-0.6.34/
|
||||||
|
python setup.py install &> /dev/null
|
||||||
|
cd $WORKING_DIR
|
||||||
|
|
||||||
|
puts-step "Installing Pip (1.2.1)"
|
||||||
|
cd $ROOT_DIR/vendor/pip-1.2.1/
|
||||||
|
python setup.py install &> /dev/null
|
||||||
|
cd $WORKING_DIR
|
||||||
|
|
||||||
|
hash -r
|
||||||
|
else
|
||||||
|
puts-step "Using Python runtime ($PYTHON_VERSION)"
|
||||||
|
fi
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -159,46 +146,42 @@ set -e
|
|||||||
# See [`bin/steps/pylibmc`](pylibmc.html).
|
# See [`bin/steps/pylibmc`](pylibmc.html).
|
||||||
source $BIN_DIR/steps/pylibmc
|
source $BIN_DIR/steps/pylibmc
|
||||||
|
|
||||||
# Activate the Virtualenv.
|
|
||||||
puts-step "Activating virtualenv"
|
|
||||||
source $VIRTUALENV_LOC/bin/activate
|
|
||||||
|
|
||||||
# Install Mercurial if it appears to be required.
|
# Install Mercurial if it appears to be required.
|
||||||
if (grep -Fiq "hg+" requirements.txt) then
|
if (grep -Fiq "hg+" requirements.txt) then
|
||||||
pip install --use-mirrors mercurial | indent
|
pip install --use-mirrors mercurial | cleanup | indent
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install dependencies with Pip.
|
# Install dependencies with Pip.
|
||||||
puts-step "Installing dependencies using Pip version $(pip --version | awk '{print $2}')"
|
puts-step "Installing dependencies using Pip (1.2.1)"
|
||||||
pip install --use-mirrors -r requirements.txt --src ./.heroku/src | indent
|
pip install --use-mirrors -r requirements.txt --exists-action=w --src=./.heroku/src | cleanup | indent
|
||||||
|
|
||||||
# Do additional application hackery if applications appears to be a Django app.
|
# Django collectstatic support.
|
||||||
# Optionally, disable all Django-specific changes with `DISABLE_INJECTION` env.
|
source $BIN_DIR/steps/collectstatic
|
||||||
#
|
|
||||||
# See [`bin/steps/django`](django.html).
|
|
||||||
|
|
||||||
if [ "$NAME" = "Python/Django" ] && ! [ "$DISABLE_INJECTION" ]; then
|
|
||||||
source $BIN_DIR/steps/django
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Make Virtualenv's paths relative for portability.
|
|
||||||
set +e
|
|
||||||
OUT=$(virtualenv --python $PYTHON_EXE --relocatable $VIRTUALENV_LOC)
|
|
||||||
[ $? -ne 0 ] && {
|
|
||||||
puts-warn "Error making virtualenv relocatable"
|
|
||||||
echo "$OUT" | indent
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# ### Finalize
|
# ### Finalize
|
||||||
#
|
#
|
||||||
|
|
||||||
# Store new artifacts in cache.
|
# Store new artifacts in cache.
|
||||||
for dir in $CACHED_DIRS; do
|
for dir in $CACHED_DIRS; do
|
||||||
|
|
||||||
rm -rf $CACHE_DIR/$dir
|
rm -rf $CACHE_DIR/$dir
|
||||||
cp -R $dir $CACHE_DIR/
|
cp -R $dir $CACHE_DIR/
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Set context environment variables.
|
||||||
|
set-env PATH '$HOME/.heroku/python/bin:$PATH'
|
||||||
|
set-env PYTHONUNBUFFERED true
|
||||||
|
set-env PYTHONHOME /app/.heroku/python
|
||||||
|
set-default-env LIBRARY_PATH /app/.heroku/vendor/lib
|
||||||
|
set-default-env LD_LIBRARY_PATH /app/.heroku/vendor/lib
|
||||||
|
set-default-env LANG en_US.UTF-8
|
||||||
|
set-default-env PYTHONHASHSEED random
|
||||||
|
set-default-env PYTHONPATH /app/
|
||||||
|
|
||||||
|
|
||||||
|
# Experimental post_compile hook.
|
||||||
|
source $BIN_DIR/steps/hooks/post_compile
|
||||||
|
|
||||||
# ### Fin.
|
# ### Fin.
|
||||||
|
|
||||||
|
deep-mv $BUILD_DIR $ORIG_BUILD_DIR
|
||||||
|
deep-mv $TMP_APP_DIR $APP_DIR
|
||||||
|
|||||||
+1
-12
@@ -19,15 +19,4 @@ if [ ! -f $BUILD_DIR/requirements.txt ] && [ ! -f $BUILD_DIR/setup.py ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If only `setup.py`, assume that the app is not Django.
|
echo Python
|
||||||
if [ ! -f $BUILD_DIR/requirements.txt ]; then
|
|
||||||
echo Python
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# `Python/Django` if `**/settings.py` is present and `django` exists in
|
|
||||||
# `requirements.txt`.
|
|
||||||
#
|
|
||||||
# Otherwise, `Python`.
|
|
||||||
|
|
||||||
ls $BUILD_DIR/**/settings.py &> /dev/null && (grep -Fiq "django" $BUILD_DIR/requirements.txt) && echo Python/Django || echo Python
|
|
||||||
|
|||||||
+6
-18
@@ -3,33 +3,21 @@
|
|||||||
|
|
||||||
BIN_DIR=$(cd $(dirname $0); pwd) # absolute path
|
BIN_DIR=$(cd $(dirname $0); pwd) # absolute path
|
||||||
BUILD_DIR=$1
|
BUILD_DIR=$1
|
||||||
NAME=$($BIN_DIR/detect $BUILD_DIR) || exit 1
|
|
||||||
|
MANAGE_FILE=$(cd $BUILD_DIR && find . -maxdepth 3 -type f -name 'manage.py' | head -1)
|
||||||
|
MANAGE_FILE=${MANAGE_FILE:2}
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
---
|
---
|
||||||
config_vars:
|
config_vars:
|
||||||
PATH: /app/.heroku/venv/bin:/bin:/usr/local/bin:/usr/bin
|
|
||||||
PYTHONUNBUFFERED: true
|
|
||||||
LIBRARY_PATH: /app/.heroku/vendor/lib
|
|
||||||
LD_LIBRARY_PATH: /app/.heroku/vendor/lib
|
|
||||||
LANG: en_US.UTF-8
|
|
||||||
PYTHONHASHSEED: random
|
|
||||||
PYTHONHOME: /app/.heroku/venv/
|
|
||||||
PYTHONPATH: /app/
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
||||||
[ "$NAME" = "Python/Django" ] || exit 0
|
if [[ $MANAGE_FILE ]]; then
|
||||||
|
|
||||||
MANAGE_FILE=$(cd $BUILD_DIR && find . -maxdepth 2 -type f -name 'manage.py' | head -1)
|
|
||||||
MANAGE_FILE=${MANAGE_FILE:2}
|
|
||||||
PROJECT=$(dirname $MANAGE_FILE)
|
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
shared-database:5mb
|
shared-database:5mb
|
||||||
|
|
||||||
default_process_types:
|
|
||||||
web: python $PROJECT/manage.py runserver 0.0.0.0:\$PORT --noreload
|
|
||||||
EOF
|
EOF
|
||||||
|
fi
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Syntax sugar.
|
||||||
|
indent() {
|
||||||
|
RE="s/^/ /"
|
||||||
|
[ $(uname) == "Darwin" ] && sed -l "$RE" || sed -u "$RE"
|
||||||
|
}
|
||||||
|
|
||||||
|
MANAGE_FILE=$(find . -maxdepth 3 -type f -name 'manage.py' | head -1)
|
||||||
|
MANAGE_FILE=${MANAGE_FILE:2}
|
||||||
|
|
||||||
|
if [ -f .heroku/collectstatic_disabled ]; then
|
||||||
|
DISABLE_COLLECTSTATIC=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! "$DISABLE_COLLECTSTATIC" ]; then
|
||||||
|
set +e
|
||||||
|
|
||||||
|
# Check if collectstatic is configured properly.
|
||||||
|
python $MANAGE_FILE collectstatic --dry-run --noinput &> /dev/null && RUN_COLLECTSTATIC=true
|
||||||
|
|
||||||
|
# Compile assets if collectstatic appears to be kosher.
|
||||||
|
if [ "$RUN_COLLECTSTATIC" ]; then
|
||||||
|
|
||||||
|
echo "-----> Collecting static files"
|
||||||
|
python $MANAGE_FILE collectstatic --noinput 2>&1 | sed '/^Copying/d;/^$/d;/^ /d' | indent
|
||||||
|
|
||||||
|
[ $? -ne 0 ] && {
|
||||||
|
echo " ! Error running manage.py collectstatic. More info:"
|
||||||
|
echo " http://devcenter.heroku.com/articles/django-assets"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# This script serves as the Django build step of the
|
|
||||||
# [**Python Buildpack**](https://github.com/heroku/heroku-buildpack-python)
|
|
||||||
# compiler.
|
|
||||||
#
|
|
||||||
# A [buildpack](http://devcenter.heroku.com/articles/buildpacks) is an
|
|
||||||
# adapter between a Python application and Heroku's runtime.
|
|
||||||
#
|
|
||||||
# This script is invoked by [`bin/compile`](/).
|
|
||||||
|
|
||||||
# ## Sanity Checks
|
|
||||||
#
|
|
||||||
|
|
||||||
# Reject a Django app that appears to be packaged incorrectly.
|
|
||||||
if [ -f settings.py ]; then
|
|
||||||
echo " ! Django app must be in a package subdirectory"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "-----> Injecting Django settings..."
|
|
||||||
|
|
||||||
|
|
||||||
SETTINGS_FILE=$(find . -maxdepth 2 -type f -name 'settings.py' | head -1)
|
|
||||||
PROJECT=$(dirname $SETTINGS_FILE)
|
|
||||||
|
|
||||||
echo "Injecting code into $SETTINGS_FILE to read from DATABASE_URL" | indent
|
|
||||||
|
|
||||||
cat >>$SETTINGS_FILE <<EOF
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import urlparse
|
|
||||||
|
|
||||||
# Register database schemes in URLs.
|
|
||||||
urlparse.uses_netloc.append('postgres')
|
|
||||||
urlparse.uses_netloc.append('mysql')
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
# Check to make sure DATABASES is set in settings.py file.
|
|
||||||
# If not default to {}
|
|
||||||
|
|
||||||
if 'DATABASES' not in locals():
|
|
||||||
DATABASES = {}
|
|
||||||
|
|
||||||
if 'DATABASE_URL' in os.environ:
|
|
||||||
url = urlparse.urlparse(os.environ['DATABASE_URL'])
|
|
||||||
|
|
||||||
# Ensure default database exists.
|
|
||||||
DATABASES['default'] = DATABASES.get('default', {})
|
|
||||||
|
|
||||||
# Update with environment configuration.
|
|
||||||
DATABASES['default'].update({
|
|
||||||
'NAME': url.path[1:],
|
|
||||||
'USER': url.username,
|
|
||||||
'PASSWORD': url.password,
|
|
||||||
'HOST': url.hostname,
|
|
||||||
'PORT': url.port,
|
|
||||||
})
|
|
||||||
if url.scheme == 'postgres':
|
|
||||||
DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql_psycopg2'
|
|
||||||
|
|
||||||
if url.scheme == 'mysql':
|
|
||||||
DATABASES['default']['ENGINE'] = 'django.db.backends.mysql'
|
|
||||||
except Exception:
|
|
||||||
print 'Unexpected error:', sys.exc_info()
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ -f bin/post_compile ]; then
|
||||||
|
echo "-----> Running post-compile hook"
|
||||||
|
chmod +x bin/post_compile
|
||||||
|
bin/post_compile
|
||||||
|
fi
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ -f bin/pre_compile ]; then
|
||||||
|
echo "-----> Running pre-compile hook"
|
||||||
|
chmod +x bin/pre_compile
|
||||||
|
bin/pre_compile
|
||||||
|
fi
|
||||||
Regular → Executable
+3
@@ -12,6 +12,9 @@
|
|||||||
# The location of the pre-compiled libmemcached binary.
|
# The location of the pre-compiled libmemcached binary.
|
||||||
VENDORED_MEMCACHED="http://cl.ly/0a191R3K160t1w1P0N25/vendor-libmemcached.tar.gz"
|
VENDORED_MEMCACHED="http://cl.ly/0a191R3K160t1w1P0N25/vendor-libmemcached.tar.gz"
|
||||||
|
|
||||||
|
# Syntax sugar.
|
||||||
|
source $BIN_DIR/utils
|
||||||
|
|
||||||
# If pylibmc exists within requirements, use vendored libmemcached.
|
# If pylibmc exists within requirements, use vendored libmemcached.
|
||||||
if (grep -Fiq "pylibmc" requirements.txt) then
|
if (grep -Fiq "pylibmc" requirements.txt) then
|
||||||
echo "-----> Noticed pylibmc. Bootstrapping libmemcached."
|
echo "-----> Noticed pylibmc. Bootstrapping libmemcached."
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ testDetectWithEmptyReqs() {
|
|||||||
|
|
||||||
testDetectDjango14() {
|
testDetectDjango14() {
|
||||||
detect "django-1.4-skeleton"
|
detect "django-1.4-skeleton"
|
||||||
assertCapturedEquals "Python/Django"
|
assertCapturedEquals "Python"
|
||||||
assertCapturedSuccess
|
assertCapturedSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
testDetectDjango13() {
|
testDetectDjango13() {
|
||||||
detect "django-1.3-skeleton"
|
detect "django-1.3-skeleton"
|
||||||
assertCapturedEquals "Python/Django"
|
assertCapturedEquals "Python"
|
||||||
assertCapturedSuccess
|
assertCapturedSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Script started on Wed Dec 19 22:40:46 2012
|
||||||
|
[1m[7m%[27m[1m[0m
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
shopt -s extglob
|
||||||
|
|
||||||
|
[ $(uname) == "Darwin" ] && SED_FLAG='-l' || SED_FLAG='-u'
|
||||||
|
|
||||||
|
# Syntax sugar.
|
||||||
|
indent() {
|
||||||
|
RE="s/^/ /"
|
||||||
|
sed $SED_FLAG "$RE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up pip output
|
||||||
|
cleanup() {
|
||||||
|
sed $SED_FLAG -e 's/\.\.\.\+/.../g' | sed $SED_FLAG '/already satisfied/Id' | sed $SED_FLAG -e '/Overwriting/Id' | sed $SED_FLAG -e '/python executable/Id' | sed $SED_FLAG -e '/no previously-included files/Id'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Buildpack Steps.
|
||||||
|
function puts-step (){
|
||||||
|
echo "-----> $@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Buildpack Warnings.
|
||||||
|
function puts-warn (){
|
||||||
|
echo " ! $@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: $ set-env key value
|
||||||
|
function set-env (){
|
||||||
|
echo "export $1=$2" >> $PROFILE_PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: $ set-default-env key value
|
||||||
|
function set-default-env (){
|
||||||
|
echo "export $1=\${$1:-$2}" >> $PROFILE_PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: $ set-default-env key value
|
||||||
|
function un-set-env (){
|
||||||
|
echo "unset $1" >> $PROFILE_PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
# Does some serious copying.
|
||||||
|
function deep-cp (){
|
||||||
|
find -H $1 -maxdepth 1 -name '.*' -a \( -type d -o -type f -o -type l \) -exec cp -a '{}' $2 \;
|
||||||
|
cp -r $1/!(tmp) $2
|
||||||
|
# echo copying $1 to $2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Does some serious moving.
|
||||||
|
function deep-mv (){
|
||||||
|
deep-cp $1 $2
|
||||||
|
|
||||||
|
rm -fr $1/!(tmp)
|
||||||
|
find -H $1 -maxdepth 1 -name '.*' -a \( -type d -o -type f -o -type l \) -exec rm -fr '{}' \;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
requests
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,2 @@
|
|||||||
|
pylibmc
|
||||||
|
psycopg2
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
requests==1.0.3
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
python-2.7.3
|
||||||
Vendored
+487
@@ -0,0 +1,487 @@
|
|||||||
|
=======
|
||||||
|
CHANGES
|
||||||
|
=======
|
||||||
|
|
||||||
|
----------
|
||||||
|
Unreleased
|
||||||
|
----------
|
||||||
|
|
||||||
|
+ Issue #341: 0.6.33 fails to build under python 2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.33
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fix 2 errors with Jython 2.5.
|
||||||
|
* Fix 1 failure with Jython 2.5 and 2.7.
|
||||||
|
* Disable workaround for Jython scripts on Linux systems.
|
||||||
|
* Issue #336: `setup.py` no longer masks failure exit code when tests fail.
|
||||||
|
* Fix issue in pkg_resources where try/except around a platform-dependent
|
||||||
|
import would trigger hook load failures on Mercurial. See pull request 32
|
||||||
|
for details.
|
||||||
|
* Issue #341: Fix a ResourceWarning.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.32
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fix test suite with Python 2.6.
|
||||||
|
* Fix some DeprecationWarnings and ResourceWarnings.
|
||||||
|
* Issue #335: Backed out `setup_requires` superceding installed requirements
|
||||||
|
until regression can be addressed.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.31
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #303: Make sure the manifest only ever contains UTF-8 in Python 3.
|
||||||
|
* Issue #329: Properly close files created by tests for compatibility with
|
||||||
|
Jython.
|
||||||
|
* Work around Jython bugs `#1980 <http://bugs.jython.org/issue1980>`_ and
|
||||||
|
`#1981 <http://bugs.jython.org/issue1981>`_.
|
||||||
|
* Issue #334: Provide workaround for packages that reference `sys.__stdout__`
|
||||||
|
such as numpy does. This change should address
|
||||||
|
`virtualenv #359 <https://github.com/pypa/virtualenv/issues/359>`_ as long
|
||||||
|
as the system encoding is UTF-8 or the IO encoding is specified in the
|
||||||
|
environment, i.e.::
|
||||||
|
|
||||||
|
PYTHONIOENCODING=utf8 pip install numpy
|
||||||
|
|
||||||
|
* Fix for encoding issue when installing from Windows executable on Python 3.
|
||||||
|
* Issue #323: Allow `setup_requires` requirements to supercede installed
|
||||||
|
requirements. Added some new keyword arguments to existing pkg_resources
|
||||||
|
methods. Also had to updated how __path__ is handled for namespace packages
|
||||||
|
to ensure that when a new egg distribution containing a namespace package is
|
||||||
|
placed on sys.path, the entries in __path__ are found in the same order they
|
||||||
|
would have been in had that egg been on the path when pkg_resources was
|
||||||
|
first imported.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.30
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #328: Clean up temporary directories in distribute_setup.py.
|
||||||
|
* Fix fatal bug in distribute_setup.py.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.29
|
||||||
|
------
|
||||||
|
|
||||||
|
* Pull Request #14: Honor file permissions in zip files.
|
||||||
|
* Issue #327: Merged pull request #24 to fix a dependency problem with pip.
|
||||||
|
* Merged pull request #23 to fix https://github.com/pypa/virtualenv/issues/301.
|
||||||
|
* If Sphinx is installed, the `upload_docs` command now runs `build_sphinx`
|
||||||
|
to produce uploadable documentation.
|
||||||
|
* Issue #326: `upload_docs` provided mangled auth credentials under Python 3.
|
||||||
|
* Issue #320: Fix check for "createable" in distribute_setup.py.
|
||||||
|
* Issue #305: Remove a warning that was triggered during normal operations.
|
||||||
|
* Issue #311: Print metadata in UTF-8 independent of platform.
|
||||||
|
* Issue #303: Read manifest file with UTF-8 encoding under Python 3.
|
||||||
|
* Issue #301: Allow to run tests of namespace packages when using 2to3.
|
||||||
|
* Issue #304: Prevent import loop in site.py under Python 3.3.
|
||||||
|
* Issue #283: Reenable scanning of `*.pyc` / `*.pyo` files on Python 3.3.
|
||||||
|
* Issue #299: The develop command didn't work on Python 3, when using 2to3,
|
||||||
|
as the egg link would go to the Python 2 source. Linking to the 2to3'd code
|
||||||
|
in build/lib makes it work, although you will have to rebuild the module
|
||||||
|
before testing it.
|
||||||
|
* Issue #306: Even if 2to3 is used, we build in-place under Python 2.
|
||||||
|
* Issue #307: Prints the full path when .svn/entries is broken.
|
||||||
|
* Issue #313: Support for sdist subcommands (Python 2.7)
|
||||||
|
* Issue #314: test_local_index() would fail an OS X.
|
||||||
|
* Issue #310: Non-ascii characters in a namespace __init__.py causes errors.
|
||||||
|
* Issue #218: Improved documentation on behavior of `package_data` and
|
||||||
|
`include_package_data`. Files indicated by `package_data` are now included
|
||||||
|
in the manifest.
|
||||||
|
* `distribute_setup.py` now allows a `--download-base` argument for retrieving
|
||||||
|
distribute from a specified location.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.28
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #294: setup.py can now be invoked from any directory.
|
||||||
|
* Scripts are now installed honoring the umask.
|
||||||
|
* Added support for .dist-info directories.
|
||||||
|
* Issue #283: Fix and disable scanning of `*.pyc` / `*.pyo` files on
|
||||||
|
Python 3.3.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.27
|
||||||
|
------
|
||||||
|
|
||||||
|
* Support current snapshots of CPython 3.3.
|
||||||
|
* Distribute now recognizes README.rst as a standard, default readme file.
|
||||||
|
* Exclude 'encodings' modules when removing modules from sys.modules.
|
||||||
|
Workaround for #285.
|
||||||
|
* Issue #231: Don't fiddle with system python when used with buildout
|
||||||
|
(bootstrap.py)
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.26
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #183: Symlinked files are now extracted from source distributions.
|
||||||
|
* Issue #227: Easy_install fetch parameters are now passed during the
|
||||||
|
installation of a source distribution; now fulfillment of setup_requires
|
||||||
|
dependencies will honor the parameters passed to easy_install.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.25
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #258: Workaround a cache issue
|
||||||
|
* Issue #260: distribute_setup.py now accepts the --user parameter for
|
||||||
|
Python 2.6 and later.
|
||||||
|
* Issue #262: package_index.open_with_auth no longer throws LookupError
|
||||||
|
on Python 3.
|
||||||
|
* Issue #269: AttributeError when an exception occurs reading Manifest.in
|
||||||
|
on late releases of Python.
|
||||||
|
* Issue #272: Prevent TypeError when namespace package names are unicode
|
||||||
|
and single-install-externally-managed is used. Also fixes PIP issue
|
||||||
|
449.
|
||||||
|
* Issue #273: Legacy script launchers now install with Python2/3 support.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.24
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #249: Added options to exclude 2to3 fixers
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.23
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #244: Fixed a test
|
||||||
|
* Issue #243: Fixed a test
|
||||||
|
* Issue #239: Fixed a test
|
||||||
|
* Issue #240: Fixed a test
|
||||||
|
* Issue #241: Fixed a test
|
||||||
|
* Issue #237: Fixed a test
|
||||||
|
* Issue #238: easy_install now uses 64bit executable wrappers on 64bit Python
|
||||||
|
* Issue #208: Fixed parsed_versions, it now honors post-releases as noted in the documentation
|
||||||
|
* Issue #207: Windows cli and gui wrappers pass CTRL-C to child python process
|
||||||
|
* Issue #227: easy_install now passes its arguments to setup.py bdist_egg
|
||||||
|
* Issue #225: Fixed a NameError on Python 2.5, 2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.21
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #225: FIxed a regression on py2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.20
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue #135: Include url in warning when processing URLs in package_index.
|
||||||
|
* Issue #212: Fix issue where easy_instal fails on Python 3 on windows installer.
|
||||||
|
* Issue #213: Fix typo in documentation.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.19
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue 206: AttributeError: 'HTTPMessage' object has no attribute 'getheaders'
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.18
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue 210: Fixed a regression introduced by Issue 204 fix.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.17
|
||||||
|
------
|
||||||
|
|
||||||
|
* Support 'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT' environment
|
||||||
|
variable to allow to disable installation of easy_install-${version} script.
|
||||||
|
* Support Python >=3.1.4 and >=3.2.1.
|
||||||
|
* Issue 204: Don't try to import the parent of a namespace package in
|
||||||
|
declare_namespace
|
||||||
|
* Issue 196: Tolerate responses with multiple Content-Length headers
|
||||||
|
* Issue 205: Sandboxing doesn't preserve working_set. Leads to setup_requires
|
||||||
|
problems.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.16
|
||||||
|
------
|
||||||
|
|
||||||
|
* Builds sdist gztar even on Windows (avoiding Issue 193).
|
||||||
|
* Issue 192: Fixed metadata omitted on Windows when package_dir
|
||||||
|
specified with forward-slash.
|
||||||
|
* Issue 195: Cython build support.
|
||||||
|
* Issue 200: Issues with recognizing 64-bit packages on Windows.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.15
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fixed typo in bdist_egg
|
||||||
|
* Several issues under Python 3 has been solved.
|
||||||
|
* Issue 146: Fixed missing DLL files after easy_install of windows exe package.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.14
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue 170: Fixed unittest failure. Thanks to Toshio.
|
||||||
|
* Issue 171: Fixed race condition in unittests cause deadlocks in test suite.
|
||||||
|
* Issue 143: Fixed a lookup issue with easy_install.
|
||||||
|
Thanks to David and Zooko.
|
||||||
|
* Issue 174: Fixed the edit mode when its used with setuptools itself
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.13
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue 160: 2.7 gives ValueError("Invalid IPv6 URL")
|
||||||
|
* Issue 150: Fixed using ~/.local even in a --no-site-packages virtualenv
|
||||||
|
* Issue 163: scan index links before external links, and don't use the md5 when
|
||||||
|
comparing two distributions
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.12
|
||||||
|
------
|
||||||
|
|
||||||
|
* Issue 149: Fixed various failures on 2.3/2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.11
|
||||||
|
------
|
||||||
|
|
||||||
|
* Found another case of SandboxViolation - fixed
|
||||||
|
* Issue 15 and 48: Introduced a socket timeout of 15 seconds on url openings
|
||||||
|
* Added indexsidebar.html into MANIFEST.in
|
||||||
|
* Issue 108: Fixed TypeError with Python3.1
|
||||||
|
* Issue 121: Fixed --help install command trying to actually install.
|
||||||
|
* Issue 112: Added an os.makedirs so that Tarek's solution will work.
|
||||||
|
* Issue 133: Added --no-find-links to easy_install
|
||||||
|
* Added easy_install --user
|
||||||
|
* Issue 100: Fixed develop --user not taking '.' in PYTHONPATH into account
|
||||||
|
* Issue 134: removed spurious UserWarnings. Patch by VanLindberg
|
||||||
|
* Issue 138: cant_write_to_target error when setup_requires is used.
|
||||||
|
* Issue 147: respect the sys.dont_write_bytecode flag
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.10
|
||||||
|
------
|
||||||
|
|
||||||
|
* Reverted change made for the DistributionNotFound exception because
|
||||||
|
zc.buildout uses the exception message to get the name of the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.9
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Issue 90: unknown setuptools version can be added in the working set
|
||||||
|
* Issue 87: setupt.py doesn't try to convert distribute_setup.py anymore
|
||||||
|
Initial Patch by arfrever.
|
||||||
|
* Issue 89: added a side bar with a download link to the doc.
|
||||||
|
* Issue 86: fixed missing sentence in pkg_resources doc.
|
||||||
|
* Added a nicer error message when a DistributionNotFound is raised.
|
||||||
|
* Issue 80: test_develop now works with Python 3.1
|
||||||
|
* Issue 93: upload_docs now works if there is an empty sub-directory.
|
||||||
|
* Issue 70: exec bit on non-exec files
|
||||||
|
* Issue 99: now the standalone easy_install command doesn't uses a
|
||||||
|
"setup.cfg" if any exists in the working directory. It will use it
|
||||||
|
only if triggered by ``install_requires`` from a setup.py call
|
||||||
|
(install, develop, etc).
|
||||||
|
* Issue 101: Allowing ``os.devnull`` in Sandbox
|
||||||
|
* Issue 92: Fixed the "no eggs" found error with MacPort
|
||||||
|
(platform.mac_ver() fails)
|
||||||
|
* Issue 103: test_get_script_header_jython_workaround not run
|
||||||
|
anymore under py3 with C or POSIX local. Contributed by Arfrever.
|
||||||
|
* Issue 104: remvoved the assertion when the installation fails,
|
||||||
|
with a nicer message for the end user.
|
||||||
|
* Issue 100: making sure there's no SandboxViolation when
|
||||||
|
the setup script patches setuptools.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.8
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added "check_packages" in dist. (added in Setuptools 0.6c11)
|
||||||
|
* Fixed the DONT_PATCH_SETUPTOOLS state.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.7
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Issue 58: Added --user support to the develop command
|
||||||
|
* Issue 11: Generated scripts now wrap their call to the script entry point
|
||||||
|
in the standard "if name == 'main'"
|
||||||
|
* Added the 'DONT_PATCH_SETUPTOOLS' environment variable, so virtualenv
|
||||||
|
can drive an installation that doesn't patch a global setuptools.
|
||||||
|
* Reviewed unladen-swallow specific change from
|
||||||
|
http://code.google.com/p/unladen-swallow/source/detail?spec=svn875&r=719
|
||||||
|
and determined that it no longer applies. Distribute should work fine with
|
||||||
|
Unladen Swallow 2009Q3.
|
||||||
|
* Issue 21: Allow PackageIndex.open_url to gracefully handle all cases of a
|
||||||
|
httplib.HTTPException instead of just InvalidURL and BadStatusLine.
|
||||||
|
* Removed virtual-python.py from this distribution and updated documentation
|
||||||
|
to point to the actively maintained virtualenv instead.
|
||||||
|
* Issue 64: use_setuptools no longer rebuilds the distribute egg every
|
||||||
|
time it is run
|
||||||
|
* use_setuptools now properly respects the requested version
|
||||||
|
* use_setuptools will no longer try to import a distribute egg for the
|
||||||
|
wrong Python version
|
||||||
|
* Issue 74: no_fake should be True by default.
|
||||||
|
* Issue 72: avoid a bootstrapping issue with easy_install -U
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.6
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Unified the bootstrap file so it works on both py2.x and py3k without 2to3
|
||||||
|
(patch by Holger Krekel)
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.5
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Issue 65: cli.exe and gui.exe are now generated at build time,
|
||||||
|
depending on the platform in use.
|
||||||
|
|
||||||
|
* Issue 67: Fixed doc typo (PEP 381/382)
|
||||||
|
|
||||||
|
* Distribute no longer shadows setuptools if we require a 0.7-series
|
||||||
|
setuptools. And an error is raised when installing a 0.7 setuptools with
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
* When run from within buildout, no attempt is made to modify an existing
|
||||||
|
setuptools egg, whether in a shared egg directory or a system setuptools.
|
||||||
|
|
||||||
|
* Fixed a hole in sandboxing allowing builtin file to write outside of
|
||||||
|
the sandbox.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.4
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added the generation of `distribute_setup_3k.py` during the release.
|
||||||
|
This closes issue #52.
|
||||||
|
|
||||||
|
* Added an upload_docs command to easily upload project documentation to
|
||||||
|
PyPI's http://packages.python.org. This close issue #56.
|
||||||
|
|
||||||
|
* Fixed a bootstrap bug on the use_setuptools() API.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.3
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Fixed a bunch of calls to file() that caused crashes on Python 3.
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Fixed a bug in sorting that caused bootstrap to fail on Python 3.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.2
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Added Python 3 support; see docs/python3.txt.
|
||||||
|
This closes http://bugs.python.org/setuptools/issue39.
|
||||||
|
|
||||||
|
* Added option to run 2to3 automatically when installing on Python 3.
|
||||||
|
This closes issue #31.
|
||||||
|
|
||||||
|
* Fixed invalid usage of requirement.parse, that broke develop -d.
|
||||||
|
This closes http://bugs.python.org/setuptools/issue44.
|
||||||
|
|
||||||
|
* Fixed script launcher for 64-bit Windows.
|
||||||
|
This closes http://bugs.python.org/setuptools/issue2.
|
||||||
|
|
||||||
|
* KeyError when compiling extensions.
|
||||||
|
This closes http://bugs.python.org/setuptools/issue41.
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Fixed bootstrap not working on Windows. This closes issue #49.
|
||||||
|
|
||||||
|
* Fixed 2.6 dependencies. This closes issue #50.
|
||||||
|
|
||||||
|
* Make sure setuptools is patched when running through easy_install
|
||||||
|
This closes http://bugs.python.org/setuptools/issue40.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.1
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* package_index.urlopen now catches BadStatusLine and malformed url errors.
|
||||||
|
This closes issue #16 and issue #18.
|
||||||
|
|
||||||
|
* zip_ok is now False by default. This closes
|
||||||
|
http://bugs.python.org/setuptools/issue33.
|
||||||
|
|
||||||
|
* Fixed invalid URL error catching. http://bugs.python.org/setuptools/issue20.
|
||||||
|
|
||||||
|
* Fixed invalid bootstraping with easy_install installation (issue #40).
|
||||||
|
Thanks to Florian Schulze for the help.
|
||||||
|
|
||||||
|
* Removed buildout/bootstrap.py. A new repository will create a specific
|
||||||
|
bootstrap.py script.
|
||||||
|
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* The boostrap process leave setuptools alone if detected in the system
|
||||||
|
and --root or --prefix is provided, but is not in the same location.
|
||||||
|
This closes issue #10.
|
||||||
|
|
||||||
|
---
|
||||||
|
0.6
|
||||||
|
---
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Packages required at build time where not fully present at install time.
|
||||||
|
This closes issue #12.
|
||||||
|
|
||||||
|
* Protected against failures in tarfile extraction. This closes issue #10.
|
||||||
|
|
||||||
|
* Made Jython api_tests.txt doctest compatible. This closes issue #7.
|
||||||
|
|
||||||
|
* sandbox.py replaced builtin type file with builtin function open. This
|
||||||
|
closes issue #6.
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes issue #3.
|
||||||
|
|
||||||
|
* Added compatibility with Subversion 1.6. This references issue #1.
|
||||||
|
|
||||||
|
pkg_resources
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Avoid a call to /usr/bin/sw_vers on OSX and use the official platform API
|
||||||
|
instead. Based on a patch from ronaldoussoren. This closes issue #5.
|
||||||
|
|
||||||
|
* Fixed a SandboxViolation for mkdir that could occur in certain cases.
|
||||||
|
This closes issue #13.
|
||||||
|
|
||||||
|
* Allow to find_on_path on systems with tight permissions to fail gracefully.
|
||||||
|
This closes issue #9.
|
||||||
|
|
||||||
|
* Corrected inconsistency between documentation and code of add_entry.
|
||||||
|
This closes issue #8.
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes issue #3.
|
||||||
|
|
||||||
|
easy_install
|
||||||
|
============
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes issue #3.
|
||||||
|
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
============
|
||||||
|
Contributors
|
||||||
|
============
|
||||||
|
|
||||||
|
* Alex Grönholm
|
||||||
|
* Alice Bevan-McGregor
|
||||||
|
* Arfrever Frehtes Taifersar Arahesis
|
||||||
|
* Christophe Combelles
|
||||||
|
* Daniel Stutzbach
|
||||||
|
* Daniel Holth
|
||||||
|
* Hanno Schlichting
|
||||||
|
* Jannis Leidel
|
||||||
|
* Jason R. Coombs
|
||||||
|
* Jim Fulton
|
||||||
|
* Jonathan Lange
|
||||||
|
* Justin Azoff
|
||||||
|
* Lennart Regebro
|
||||||
|
* Marc Abramowitz
|
||||||
|
* Martin von Löwis
|
||||||
|
* Noufal Ibrahim
|
||||||
|
* Pete Hollobon
|
||||||
|
* Philip Jenvey
|
||||||
|
* Reinout van Rees
|
||||||
|
* Robert Myers
|
||||||
|
* Stefan H. Holek
|
||||||
|
* Tarek Ziadé
|
||||||
|
* Toshio Kuratomi
|
||||||
|
|
||||||
|
If you think you name is missing, please add it (alpha order by first name)
|
||||||
|
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
============================
|
||||||
|
Quick notes for contributors
|
||||||
|
============================
|
||||||
|
|
||||||
|
Distribute is using Mercurial.
|
||||||
|
|
||||||
|
Grab the code at bitbucket::
|
||||||
|
|
||||||
|
$ hg clone https://bitbucket.org/tarek/distribute
|
||||||
|
|
||||||
|
If you want to contribute changes, we recommend you fork the repository on
|
||||||
|
bitbucket, commit the changes to your repository, and then make a pull request
|
||||||
|
on bitbucket. If you make some changes, don't forget to:
|
||||||
|
|
||||||
|
- add a note in CHANGES.txt
|
||||||
|
|
||||||
|
And remember that 0.6 (the only development line) is only bug fixes, and the
|
||||||
|
APIs should be fully backward compatible with Setuptools.
|
||||||
|
|
||||||
|
You can run the tests via::
|
||||||
|
|
||||||
|
$ python setup.py test
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
recursive-include setuptools *.py *.txt *.exe
|
||||||
|
recursive-include tests *.py *.c *.pyx *.txt
|
||||||
|
recursive-include setuptools/tests *.html
|
||||||
|
recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html
|
||||||
|
recursive-include _markerlib *.py
|
||||||
|
include *.py
|
||||||
|
include *.txt
|
||||||
|
include MANIFEST.in
|
||||||
|
include launcher.c
|
||||||
Vendored
+868
@@ -0,0 +1,868 @@
|
|||||||
|
Metadata-Version: 1.1
|
||||||
|
Name: distribute
|
||||||
|
Version: 0.6.34
|
||||||
|
Summary: Easily download, build, install, upgrade, and uninstall Python packages
|
||||||
|
Home-page: http://packages.python.org/distribute
|
||||||
|
Author: The fellowship of the packaging
|
||||||
|
Author-email: distutils-sig@python.org
|
||||||
|
License: PSF or ZPL
|
||||||
|
Description: ===============================
|
||||||
|
Installing and Using Distribute
|
||||||
|
===============================
|
||||||
|
|
||||||
|
.. contents:: **Table of Contents**
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Disclaimers
|
||||||
|
-----------
|
||||||
|
|
||||||
|
About the fork
|
||||||
|
==============
|
||||||
|
|
||||||
|
`Distribute` is a fork of the `Setuptools` project.
|
||||||
|
|
||||||
|
Distribute is intended to replace Setuptools as the standard method
|
||||||
|
for working with Python module distributions.
|
||||||
|
|
||||||
|
The fork has two goals:
|
||||||
|
|
||||||
|
- Providing a backward compatible version to replace Setuptools
|
||||||
|
and make all distributions that depend on Setuptools work as
|
||||||
|
before, but with less bugs and behaviorial issues.
|
||||||
|
|
||||||
|
This work is done in the 0.6.x series.
|
||||||
|
|
||||||
|
Starting with version 0.6.2, Distribute supports Python 3.
|
||||||
|
Installing and using distribute for Python 3 code works exactly
|
||||||
|
the same as for Python 2 code, but Distribute also helps you to support
|
||||||
|
Python 2 and Python 3 from the same source code by letting you run 2to3
|
||||||
|
on the code as a part of the build process, by setting the keyword parameter
|
||||||
|
``use_2to3`` to True. See http://packages.python.org/distribute for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
- Refactoring the code, and releasing it in several distributions.
|
||||||
|
This work is being done in the 0.7.x series but not yet released.
|
||||||
|
|
||||||
|
The roadmap is still evolving, and the page that is up-to-date is
|
||||||
|
located at : `http://packages.python.org/distribute/roadmap`.
|
||||||
|
|
||||||
|
If you install `Distribute` and want to switch back for any reason to
|
||||||
|
`Setuptools`, get to the `Uninstallation instructions`_ section.
|
||||||
|
|
||||||
|
More documentation
|
||||||
|
==================
|
||||||
|
|
||||||
|
You can get more information in the Sphinx-based documentation, located
|
||||||
|
at http://packages.python.org/distribute. This documentation includes the old
|
||||||
|
Setuptools documentation that is slowly replaced, and brand new content.
|
||||||
|
|
||||||
|
About the installation process
|
||||||
|
==============================
|
||||||
|
|
||||||
|
The `Distribute` installer modifies your installation by de-activating an
|
||||||
|
existing installation of `Setuptools` in a bootstrap process. This process
|
||||||
|
has been tested in various installation schemes and contexts but in case of a
|
||||||
|
bug during this process your Python installation might be left in a broken
|
||||||
|
state. Since all modified files and directories are copied before the
|
||||||
|
installation starts, you will be able to get back to a normal state by reading
|
||||||
|
the instructions in the `Uninstallation instructions`_ section.
|
||||||
|
|
||||||
|
In any case, it is recommended to save you `site-packages` directory before
|
||||||
|
you start the installation of `Distribute`.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
Installation Instructions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Distribute is only released as a source distribution.
|
||||||
|
|
||||||
|
It can be installed using pip, and can be done so with the source tarball,
|
||||||
|
or by using the ``distribute_setup.py`` script provided online.
|
||||||
|
|
||||||
|
``distribute_setup.py`` is the simplest and preferred way on all systems.
|
||||||
|
|
||||||
|
distribute_setup.py
|
||||||
|
===================
|
||||||
|
|
||||||
|
Download
|
||||||
|
`distribute_setup.py <http://python-distribute.org/distribute_setup.py>`_
|
||||||
|
and execute it, using the Python interpreter of your choice.
|
||||||
|
|
||||||
|
If your shell has the ``curl`` program you can do::
|
||||||
|
|
||||||
|
$ curl -O http://python-distribute.org/distribute_setup.py
|
||||||
|
$ python distribute_setup.py
|
||||||
|
|
||||||
|
Notice this file is also provided in the source release.
|
||||||
|
|
||||||
|
pip
|
||||||
|
===
|
||||||
|
|
||||||
|
Run easy_install or pip::
|
||||||
|
|
||||||
|
$ pip install distribute
|
||||||
|
|
||||||
|
Source installation
|
||||||
|
===================
|
||||||
|
|
||||||
|
Download the source tarball, uncompress it, then run the install command::
|
||||||
|
|
||||||
|
$ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.34.tar.gz
|
||||||
|
$ tar -xzvf distribute-0.6.34.tar.gz
|
||||||
|
$ cd distribute-0.6.34
|
||||||
|
$ python setup.py install
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
Uninstallation Instructions
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Like other distutils-based distributions, Distribute doesn't provide an
|
||||||
|
uninstaller yet. It's all done manually! We are all waiting for PEP 376
|
||||||
|
support in Python.
|
||||||
|
|
||||||
|
Distribute is installed in three steps:
|
||||||
|
|
||||||
|
1. it gets out of the way an existing installation of Setuptools
|
||||||
|
2. it installs a `fake` setuptools installation
|
||||||
|
3. it installs distribute
|
||||||
|
|
||||||
|
Distribute can be removed like this:
|
||||||
|
|
||||||
|
- remove the ``distribute*.egg`` file located in your site-packages directory
|
||||||
|
- remove the ``setuptools.pth`` file located in you site-packages directory
|
||||||
|
- remove the easy_install script located in you ``sys.prefix/bin`` directory
|
||||||
|
- remove the ``setuptools*.egg`` directory located in your site-packages directory,
|
||||||
|
if any.
|
||||||
|
|
||||||
|
If you want to get back to setuptools:
|
||||||
|
|
||||||
|
- reinstall setuptools using its instruction.
|
||||||
|
|
||||||
|
Lastly:
|
||||||
|
|
||||||
|
- remove the *.OLD.* directory located in your site-packages directory if any,
|
||||||
|
**once you have checked everything was working correctly again**.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
Quick help for developers
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
To create an egg which is compatible with Distribute, use the same
|
||||||
|
practice as with Setuptools, e.g.::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
)
|
||||||
|
|
||||||
|
To use `pkg_resources` to access data files in the egg, you should
|
||||||
|
require the Setuptools distribution explicitly::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
install_requires=['setuptools']
|
||||||
|
)
|
||||||
|
|
||||||
|
Only if you need Distribute-specific functionality should you depend
|
||||||
|
on it explicitly. In this case, replace the Setuptools dependency::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
install_requires=['distribute']
|
||||||
|
)
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Install FAQ
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- **Why is Distribute wrapping my Setuptools installation?**
|
||||||
|
|
||||||
|
Since Distribute is a fork, and since it provides the same package
|
||||||
|
and modules, it renames the existing Setuptools egg and inserts a
|
||||||
|
new one which merely wraps the Distribute code. This way, full
|
||||||
|
backwards compatibility is kept for packages which rely on the
|
||||||
|
Setuptools modules.
|
||||||
|
|
||||||
|
At the same time, packages can meet their dependency on Setuptools
|
||||||
|
without actually installing it (which would disable Distribute).
|
||||||
|
|
||||||
|
- **How does Distribute interact with virtualenv?**
|
||||||
|
|
||||||
|
Everytime you create a virtualenv it will install setuptools by default.
|
||||||
|
You either need to re-install Distribute in it right after or pass the
|
||||||
|
``--distribute`` option when creating it.
|
||||||
|
|
||||||
|
Once installed, your virtualenv will use Distribute transparently.
|
||||||
|
|
||||||
|
Although, if you have Setuptools installed in your system-wide Python,
|
||||||
|
and if the virtualenv you are in was generated without the `--no-site-packages`
|
||||||
|
option, the Distribute installation will stop.
|
||||||
|
|
||||||
|
You need in this case to build a virtualenv with the `--no-site-packages`
|
||||||
|
option or to install `Distribute` globally.
|
||||||
|
|
||||||
|
- **How does Distribute interacts with zc.buildout?**
|
||||||
|
|
||||||
|
You can use Distribute in your zc.buildout, with the --distribute option,
|
||||||
|
starting at zc.buildout 1.4.2::
|
||||||
|
|
||||||
|
$ python bootstrap.py --distribute
|
||||||
|
|
||||||
|
For previous zc.buildout versions, *the only thing* you need to do
|
||||||
|
is use the bootstrap at `http://python-distribute.org/bootstrap.py`. Run
|
||||||
|
that bootstrap and ``bin/buildout`` (and all other buildout-generated
|
||||||
|
scripts) will transparently use distribute instead of setuptools. You do
|
||||||
|
not need a specific buildout release.
|
||||||
|
|
||||||
|
A shared eggs directory is no problem (since 0.6.6): the setuptools egg is
|
||||||
|
left in place unmodified. So other buildouts that do not yet use the new
|
||||||
|
bootstrap continue to work just fine. And there is no need to list
|
||||||
|
``distribute`` somewhere in your eggs: using the bootstrap is enough.
|
||||||
|
|
||||||
|
The source code for the bootstrap script is located at
|
||||||
|
`http://bitbucket.org/tarek/buildout-distribute`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------
|
||||||
|
Feedback and getting involved
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Mailing list: http://mail.python.org/mailman/listinfo/distutils-sig
|
||||||
|
- Issue tracker: http://bitbucket.org/tarek/distribute/issues/
|
||||||
|
- Code Repository: http://bitbucket.org/tarek/distribute
|
||||||
|
|
||||||
|
=======
|
||||||
|
CHANGES
|
||||||
|
=======
|
||||||
|
|
||||||
|
----------
|
||||||
|
Unreleased
|
||||||
|
----------
|
||||||
|
|
||||||
|
+ `Issue #341`_: 0.6.33 fails to build under python 2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.33
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fix 2 errors with Jython 2.5.
|
||||||
|
* Fix 1 failure with Jython 2.5 and 2.7.
|
||||||
|
* Disable workaround for Jython scripts on Linux systems.
|
||||||
|
* `Issue #336`_: `setup.py` no longer masks failure exit code when tests fail.
|
||||||
|
* Fix issue in pkg_resources where try/except around a platform-dependent
|
||||||
|
import would trigger hook load failures on Mercurial. See pull request 32
|
||||||
|
for details.
|
||||||
|
* `Issue #341`_: Fix a ResourceWarning.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.32
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fix test suite with Python 2.6.
|
||||||
|
* Fix some DeprecationWarnings and ResourceWarnings.
|
||||||
|
* `Issue #335`_: Backed out `setup_requires` superceding installed requirements
|
||||||
|
until regression can be addressed.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.31
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #303`_: Make sure the manifest only ever contains UTF-8 in Python 3.
|
||||||
|
* `Issue #329`_: Properly close files created by tests for compatibility with
|
||||||
|
Jython.
|
||||||
|
* Work around Jython bugs `#1980 <http://bugs.jython.org/`issue1980`_>`_ and
|
||||||
|
`#1981 <http://bugs.jython.org/`issue1981`_>`_.
|
||||||
|
* `Issue #334`_: Provide workaround for packages that reference `sys.__stdout__`
|
||||||
|
such as numpy does. This change should address
|
||||||
|
`virtualenv #359 <https://github.com/pypa/virtualenv/issues/359>`_ as long
|
||||||
|
as the system encoding is UTF-8 or the IO encoding is specified in the
|
||||||
|
environment, i.e.::
|
||||||
|
|
||||||
|
PYTHONIOENCODING=utf8 pip install numpy
|
||||||
|
|
||||||
|
* Fix for encoding issue when installing from Windows executable on Python 3.
|
||||||
|
* `Issue #323`_: Allow `setup_requires` requirements to supercede installed
|
||||||
|
requirements. Added some new keyword arguments to existing pkg_resources
|
||||||
|
methods. Also had to updated how __path__ is handled for namespace packages
|
||||||
|
to ensure that when a new egg distribution containing a namespace package is
|
||||||
|
placed on sys.path, the entries in __path__ are found in the same order they
|
||||||
|
would have been in had that egg been on the path when pkg_resources was
|
||||||
|
first imported.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.30
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #328`_: Clean up temporary directories in distribute_setup.py.
|
||||||
|
* Fix fatal bug in distribute_setup.py.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.29
|
||||||
|
------
|
||||||
|
|
||||||
|
* Pull Request #14: Honor file permissions in zip files.
|
||||||
|
* `Issue #327`_: Merged pull request #24 to fix a dependency problem with pip.
|
||||||
|
* Merged pull request #23 to fix https://github.com/pypa/virtualenv/issues/301.
|
||||||
|
* If Sphinx is installed, the `upload_docs` command now runs `build_sphinx`
|
||||||
|
to produce uploadable documentation.
|
||||||
|
* `Issue #326`_: `upload_docs` provided mangled auth credentials under Python 3.
|
||||||
|
* `Issue #320`_: Fix check for "createable" in distribute_setup.py.
|
||||||
|
* `Issue #305`_: Remove a warning that was triggered during normal operations.
|
||||||
|
* `Issue #311`_: Print metadata in UTF-8 independent of platform.
|
||||||
|
* `Issue #303`_: Read manifest file with UTF-8 encoding under Python 3.
|
||||||
|
* `Issue #301`_: Allow to run tests of namespace packages when using 2to3.
|
||||||
|
* `Issue #304`_: Prevent import loop in site.py under Python 3.3.
|
||||||
|
* `Issue #283`_: Reenable scanning of `*.pyc` / `*.pyo` files on Python 3.3.
|
||||||
|
* `Issue #299`_: The develop command didn't work on Python 3, when using 2to3,
|
||||||
|
as the egg link would go to the Python 2 source. Linking to the 2to3'd code
|
||||||
|
in build/lib makes it work, although you will have to rebuild the module
|
||||||
|
before testing it.
|
||||||
|
* `Issue #306`_: Even if 2to3 is used, we build in-place under Python 2.
|
||||||
|
* `Issue #307`_: Prints the full path when .svn/entries is broken.
|
||||||
|
* `Issue #313`_: Support for sdist subcommands (Python 2.7)
|
||||||
|
* `Issue #314`_: test_local_index() would fail an OS X.
|
||||||
|
* `Issue #310`_: Non-ascii characters in a namespace __init__.py causes errors.
|
||||||
|
* `Issue #218`_: Improved documentation on behavior of `package_data` and
|
||||||
|
`include_package_data`. Files indicated by `package_data` are now included
|
||||||
|
in the manifest.
|
||||||
|
* `distribute_setup.py` now allows a `--download-base` argument for retrieving
|
||||||
|
distribute from a specified location.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.28
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #294`_: setup.py can now be invoked from any directory.
|
||||||
|
* Scripts are now installed honoring the umask.
|
||||||
|
* Added support for .dist-info directories.
|
||||||
|
* `Issue #283`_: Fix and disable scanning of `*.pyc` / `*.pyo` files on
|
||||||
|
Python 3.3.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.27
|
||||||
|
------
|
||||||
|
|
||||||
|
* Support current snapshots of CPython 3.3.
|
||||||
|
* Distribute now recognizes README.rst as a standard, default readme file.
|
||||||
|
* Exclude 'encodings' modules when removing modules from sys.modules.
|
||||||
|
Workaround for #285.
|
||||||
|
* `Issue #231`_: Don't fiddle with system python when used with buildout
|
||||||
|
(bootstrap.py)
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.26
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #183`_: Symlinked files are now extracted from source distributions.
|
||||||
|
* `Issue #227`_: Easy_install fetch parameters are now passed during the
|
||||||
|
installation of a source distribution; now fulfillment of setup_requires
|
||||||
|
dependencies will honor the parameters passed to easy_install.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.25
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #258`_: Workaround a cache issue
|
||||||
|
* `Issue #260`_: distribute_setup.py now accepts the --user parameter for
|
||||||
|
Python 2.6 and later.
|
||||||
|
* `Issue #262`_: package_index.open_with_auth no longer throws LookupError
|
||||||
|
on Python 3.
|
||||||
|
* `Issue #269`_: AttributeError when an exception occurs reading Manifest.in
|
||||||
|
on late releases of Python.
|
||||||
|
* `Issue #272`_: Prevent TypeError when namespace package names are unicode
|
||||||
|
and single-install-externally-managed is used. Also fixes PIP `issue
|
||||||
|
449`_.
|
||||||
|
* `Issue #273`_: Legacy script launchers now install with Python2/3 support.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.24
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #249`_: Added options to exclude 2to3 fixers
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.23
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #244`_: Fixed a test
|
||||||
|
* `Issue #243`_: Fixed a test
|
||||||
|
* `Issue #239`_: Fixed a test
|
||||||
|
* `Issue #240`_: Fixed a test
|
||||||
|
* `Issue #241`_: Fixed a test
|
||||||
|
* `Issue #237`_: Fixed a test
|
||||||
|
* `Issue #238`_: easy_install now uses 64bit executable wrappers on 64bit Python
|
||||||
|
* `Issue #208`_: Fixed parsed_versions, it now honors post-releases as noted in the documentation
|
||||||
|
* `Issue #207`_: Windows cli and gui wrappers pass CTRL-C to child python process
|
||||||
|
* `Issue #227`_: easy_install now passes its arguments to setup.py bdist_egg
|
||||||
|
* `Issue #225`_: Fixed a NameError on Python 2.5, 2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.21
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #225`_: FIxed a regression on py2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.20
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #135`_: Include url in warning when processing URLs in package_index.
|
||||||
|
* `Issue #212`_: Fix issue where easy_instal fails on Python 3 on windows installer.
|
||||||
|
* `Issue #213`_: Fix typo in documentation.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.19
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 206`_: AttributeError: 'HTTPMessage' object has no attribute 'getheaders'
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.18
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 210`_: Fixed a regression introduced by `Issue 204`_ fix.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.17
|
||||||
|
------
|
||||||
|
|
||||||
|
* Support 'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT' environment
|
||||||
|
variable to allow to disable installation of easy_install-${version} script.
|
||||||
|
* Support Python >=3.1.4 and >=3.2.1.
|
||||||
|
* `Issue 204`_: Don't try to import the parent of a namespace package in
|
||||||
|
declare_namespace
|
||||||
|
* `Issue 196`_: Tolerate responses with multiple Content-Length headers
|
||||||
|
* `Issue 205`_: Sandboxing doesn't preserve working_set. Leads to setup_requires
|
||||||
|
problems.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.16
|
||||||
|
------
|
||||||
|
|
||||||
|
* Builds sdist gztar even on Windows (avoiding `Issue 193`_).
|
||||||
|
* `Issue 192`_: Fixed metadata omitted on Windows when package_dir
|
||||||
|
specified with forward-slash.
|
||||||
|
* `Issue 195`_: Cython build support.
|
||||||
|
* `Issue 200`_: Issues with recognizing 64-bit packages on Windows.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.15
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fixed typo in bdist_egg
|
||||||
|
* Several issues under Python 3 has been solved.
|
||||||
|
* `Issue 146`_: Fixed missing DLL files after easy_install of windows exe package.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.14
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 170`_: Fixed unittest failure. Thanks to Toshio.
|
||||||
|
* `Issue 171`_: Fixed race condition in unittests cause deadlocks in test suite.
|
||||||
|
* `Issue 143`_: Fixed a lookup issue with easy_install.
|
||||||
|
Thanks to David and Zooko.
|
||||||
|
* `Issue 174`_: Fixed the edit mode when its used with setuptools itself
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.13
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 160`_: 2.7 gives ValueError("Invalid IPv6 URL")
|
||||||
|
* `Issue 150`_: Fixed using ~/.local even in a --no-site-packages virtualenv
|
||||||
|
* `Issue 163`_: scan index links before external links, and don't use the md5 when
|
||||||
|
comparing two distributions
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.12
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 149`_: Fixed various failures on 2.3/2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.11
|
||||||
|
------
|
||||||
|
|
||||||
|
* Found another case of SandboxViolation - fixed
|
||||||
|
* `Issue 15`_ and 48: Introduced a socket timeout of 15 seconds on url openings
|
||||||
|
* Added indexsidebar.html into MANIFEST.in
|
||||||
|
* `Issue 108`_: Fixed TypeError with Python3.1
|
||||||
|
* `Issue 121`_: Fixed --help install command trying to actually install.
|
||||||
|
* `Issue 112`_: Added an os.makedirs so that Tarek's solution will work.
|
||||||
|
* `Issue 133`_: Added --no-find-links to easy_install
|
||||||
|
* Added easy_install --user
|
||||||
|
* `Issue 100`_: Fixed develop --user not taking '.' in PYTHONPATH into account
|
||||||
|
* `Issue 134`_: removed spurious UserWarnings. Patch by VanLindberg
|
||||||
|
* `Issue 138`_: cant_write_to_target error when setup_requires is used.
|
||||||
|
* `Issue 147`_: respect the sys.dont_write_bytecode flag
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.10
|
||||||
|
------
|
||||||
|
|
||||||
|
* Reverted change made for the DistributionNotFound exception because
|
||||||
|
zc.buildout uses the exception message to get the name of the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.9
|
||||||
|
-----
|
||||||
|
|
||||||
|
* `Issue 90`_: unknown setuptools version can be added in the working set
|
||||||
|
* `Issue 87`_: setupt.py doesn't try to convert distribute_setup.py anymore
|
||||||
|
Initial Patch by arfrever.
|
||||||
|
* `Issue 89`_: added a side bar with a download link to the doc.
|
||||||
|
* `Issue 86`_: fixed missing sentence in pkg_resources doc.
|
||||||
|
* Added a nicer error message when a DistributionNotFound is raised.
|
||||||
|
* `Issue 80`_: test_develop now works with Python 3.1
|
||||||
|
* `Issue 93`_: upload_docs now works if there is an empty sub-directory.
|
||||||
|
* `Issue 70`_: exec bit on non-exec files
|
||||||
|
* `Issue 99`_: now the standalone easy_install command doesn't uses a
|
||||||
|
"setup.cfg" if any exists in the working directory. It will use it
|
||||||
|
only if triggered by ``install_requires`` from a setup.py call
|
||||||
|
(install, develop, etc).
|
||||||
|
* `Issue 101`_: Allowing ``os.devnull`` in Sandbox
|
||||||
|
* `Issue 92`_: Fixed the "no eggs" found error with MacPort
|
||||||
|
(platform.mac_ver() fails)
|
||||||
|
* `Issue 103`_: test_get_script_header_jython_workaround not run
|
||||||
|
anymore under py3 with C or POSIX local. Contributed by Arfrever.
|
||||||
|
* `Issue 104`_: remvoved the assertion when the installation fails,
|
||||||
|
with a nicer message for the end user.
|
||||||
|
* `Issue 100`_: making sure there's no SandboxViolation when
|
||||||
|
the setup script patches setuptools.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.8
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added "check_packages" in dist. (added in Setuptools 0.6c11)
|
||||||
|
* Fixed the DONT_PATCH_SETUPTOOLS state.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.7
|
||||||
|
-----
|
||||||
|
|
||||||
|
* `Issue 58`_: Added --user support to the develop command
|
||||||
|
* `Issue 11`_: Generated scripts now wrap their call to the script entry point
|
||||||
|
in the standard "if name == 'main'"
|
||||||
|
* Added the 'DONT_PATCH_SETUPTOOLS' environment variable, so virtualenv
|
||||||
|
can drive an installation that doesn't patch a global setuptools.
|
||||||
|
* Reviewed unladen-swallow specific change from
|
||||||
|
http://code.google.com/p/unladen-swallow/source/detail?spec=svn875&r=719
|
||||||
|
and determined that it no longer applies. Distribute should work fine with
|
||||||
|
Unladen Swallow 2009Q3.
|
||||||
|
* `Issue 21`_: Allow PackageIndex.open_url to gracefully handle all cases of a
|
||||||
|
httplib.HTTPException instead of just InvalidURL and BadStatusLine.
|
||||||
|
* Removed virtual-python.py from this distribution and updated documentation
|
||||||
|
to point to the actively maintained virtualenv instead.
|
||||||
|
* `Issue 64`_: use_setuptools no longer rebuilds the distribute egg every
|
||||||
|
time it is run
|
||||||
|
* use_setuptools now properly respects the requested version
|
||||||
|
* use_setuptools will no longer try to import a distribute egg for the
|
||||||
|
wrong Python version
|
||||||
|
* `Issue 74`_: no_fake should be True by default.
|
||||||
|
* `Issue 72`_: avoid a bootstrapping issue with easy_install -U
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.6
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Unified the bootstrap file so it works on both py2.x and py3k without 2to3
|
||||||
|
(patch by Holger Krekel)
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.5
|
||||||
|
-----
|
||||||
|
|
||||||
|
* `Issue 65`_: cli.exe and gui.exe are now generated at build time,
|
||||||
|
depending on the platform in use.
|
||||||
|
|
||||||
|
* `Issue 67`_: Fixed doc typo (PEP 381/382)
|
||||||
|
|
||||||
|
* Distribute no longer shadows setuptools if we require a 0.7-series
|
||||||
|
setuptools. And an error is raised when installing a 0.7 setuptools with
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
* When run from within buildout, no attempt is made to modify an existing
|
||||||
|
setuptools egg, whether in a shared egg directory or a system setuptools.
|
||||||
|
|
||||||
|
* Fixed a hole in sandboxing allowing builtin file to write outside of
|
||||||
|
the sandbox.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.4
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added the generation of `distribute_setup_3k.py` during the release.
|
||||||
|
This closes `issue #52`_.
|
||||||
|
|
||||||
|
* Added an upload_docs command to easily upload project documentation to
|
||||||
|
PyPI's http://packages.python.org. This close `issue #56`_.
|
||||||
|
|
||||||
|
* Fixed a bootstrap bug on the use_setuptools() API.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.3
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Fixed a bunch of calls to file() that caused crashes on Python 3.
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Fixed a bug in sorting that caused bootstrap to fail on Python 3.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.2
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Added Python 3 support; see docs/python3.txt.
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue39`_.
|
||||||
|
|
||||||
|
* Added option to run 2to3 automatically when installing on Python 3.
|
||||||
|
This closes `issue #31`_.
|
||||||
|
|
||||||
|
* Fixed invalid usage of requirement.parse, that broke develop -d.
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue44`_.
|
||||||
|
|
||||||
|
* Fixed script launcher for 64-bit Windows.
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue2`_.
|
||||||
|
|
||||||
|
* KeyError when compiling extensions.
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue41`_.
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Fixed bootstrap not working on Windows. This closes `issue #49`_.
|
||||||
|
|
||||||
|
* Fixed 2.6 dependencies. This closes `issue #50`_.
|
||||||
|
|
||||||
|
* Make sure setuptools is patched when running through easy_install
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue40`_.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.1
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* package_index.urlopen now catches BadStatusLine and malformed url errors.
|
||||||
|
This closes `issue #16`_ and `issue #18`_.
|
||||||
|
|
||||||
|
* zip_ok is now False by default. This closes
|
||||||
|
http://bugs.python.org/setuptools/`issue33`_.
|
||||||
|
|
||||||
|
* Fixed invalid URL error catching. http://bugs.python.org/setuptools/`issue20`_.
|
||||||
|
|
||||||
|
* Fixed invalid bootstraping with easy_install installation (`issue #40`_).
|
||||||
|
Thanks to Florian Schulze for the help.
|
||||||
|
|
||||||
|
* Removed buildout/bootstrap.py. A new repository will create a specific
|
||||||
|
bootstrap.py script.
|
||||||
|
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* The boostrap process leave setuptools alone if detected in the system
|
||||||
|
and --root or --prefix is provided, but is not in the same location.
|
||||||
|
This closes `issue #10`_.
|
||||||
|
|
||||||
|
---
|
||||||
|
0.6
|
||||||
|
---
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Packages required at build time where not fully present at install time.
|
||||||
|
This closes `issue #12`_.
|
||||||
|
|
||||||
|
* Protected against failures in tarfile extraction. This closes `issue #10`_.
|
||||||
|
|
||||||
|
* Made Jython api_tests.txt doctest compatible. This closes `issue #7`_.
|
||||||
|
|
||||||
|
* sandbox.py replaced builtin type file with builtin function open. This
|
||||||
|
closes `issue #6`_.
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes `issue #3`_.
|
||||||
|
|
||||||
|
* Added compatibility with Subversion 1.6. This references `issue #1`_.
|
||||||
|
|
||||||
|
pkg_resources
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Avoid a call to /usr/bin/sw_vers on OSX and use the official platform API
|
||||||
|
instead. Based on a patch from ronaldoussoren. This closes `issue #5`_.
|
||||||
|
|
||||||
|
* Fixed a SandboxViolation for mkdir that could occur in certain cases.
|
||||||
|
This closes `issue #13`_.
|
||||||
|
|
||||||
|
* Allow to find_on_path on systems with tight permissions to fail gracefully.
|
||||||
|
This closes `issue #9`_.
|
||||||
|
|
||||||
|
* Corrected inconsistency between documentation and code of add_entry.
|
||||||
|
This closes `issue #8`_.
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes `issue #3`_.
|
||||||
|
|
||||||
|
easy_install
|
||||||
|
============
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes `issue #3`_.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`Issue #135`: http://bitbucket.org/tarek/distribute/issue/135
|
||||||
|
.. _`Issue #183`: http://bitbucket.org/tarek/distribute/issue/183
|
||||||
|
.. _`Issue #207`: http://bitbucket.org/tarek/distribute/issue/207
|
||||||
|
.. _`Issue #208`: http://bitbucket.org/tarek/distribute/issue/208
|
||||||
|
.. _`Issue #212`: http://bitbucket.org/tarek/distribute/issue/212
|
||||||
|
.. _`Issue #213`: http://bitbucket.org/tarek/distribute/issue/213
|
||||||
|
.. _`Issue #218`: http://bitbucket.org/tarek/distribute/issue/218
|
||||||
|
.. _`Issue #225`: http://bitbucket.org/tarek/distribute/issue/225
|
||||||
|
.. _`Issue #227`: http://bitbucket.org/tarek/distribute/issue/227
|
||||||
|
.. _`Issue #231`: http://bitbucket.org/tarek/distribute/issue/231
|
||||||
|
.. _`Issue #237`: http://bitbucket.org/tarek/distribute/issue/237
|
||||||
|
.. _`Issue #238`: http://bitbucket.org/tarek/distribute/issue/238
|
||||||
|
.. _`Issue #239`: http://bitbucket.org/tarek/distribute/issue/239
|
||||||
|
.. _`Issue #240`: http://bitbucket.org/tarek/distribute/issue/240
|
||||||
|
.. _`Issue #241`: http://bitbucket.org/tarek/distribute/issue/241
|
||||||
|
.. _`Issue #243`: http://bitbucket.org/tarek/distribute/issue/243
|
||||||
|
.. _`Issue #244`: http://bitbucket.org/tarek/distribute/issue/244
|
||||||
|
.. _`Issue #249`: http://bitbucket.org/tarek/distribute/issue/249
|
||||||
|
.. _`Issue #258`: http://bitbucket.org/tarek/distribute/issue/258
|
||||||
|
.. _`Issue #260`: http://bitbucket.org/tarek/distribute/issue/260
|
||||||
|
.. _`Issue #262`: http://bitbucket.org/tarek/distribute/issue/262
|
||||||
|
.. _`Issue #269`: http://bitbucket.org/tarek/distribute/issue/269
|
||||||
|
.. _`Issue #272`: http://bitbucket.org/tarek/distribute/issue/272
|
||||||
|
.. _`Issue #273`: http://bitbucket.org/tarek/distribute/issue/273
|
||||||
|
.. _`Issue #283`: http://bitbucket.org/tarek/distribute/issue/283
|
||||||
|
.. _`Issue #294`: http://bitbucket.org/tarek/distribute/issue/294
|
||||||
|
.. _`Issue #299`: http://bitbucket.org/tarek/distribute/issue/299
|
||||||
|
.. _`Issue #301`: http://bitbucket.org/tarek/distribute/issue/301
|
||||||
|
.. _`Issue #303`: http://bitbucket.org/tarek/distribute/issue/303
|
||||||
|
.. _`Issue #304`: http://bitbucket.org/tarek/distribute/issue/304
|
||||||
|
.. _`Issue #305`: http://bitbucket.org/tarek/distribute/issue/305
|
||||||
|
.. _`Issue #306`: http://bitbucket.org/tarek/distribute/issue/306
|
||||||
|
.. _`Issue #307`: http://bitbucket.org/tarek/distribute/issue/307
|
||||||
|
.. _`Issue #310`: http://bitbucket.org/tarek/distribute/issue/310
|
||||||
|
.. _`Issue #311`: http://bitbucket.org/tarek/distribute/issue/311
|
||||||
|
.. _`Issue #313`: http://bitbucket.org/tarek/distribute/issue/313
|
||||||
|
.. _`Issue #314`: http://bitbucket.org/tarek/distribute/issue/314
|
||||||
|
.. _`Issue #320`: http://bitbucket.org/tarek/distribute/issue/320
|
||||||
|
.. _`Issue #323`: http://bitbucket.org/tarek/distribute/issue/323
|
||||||
|
.. _`Issue #326`: http://bitbucket.org/tarek/distribute/issue/326
|
||||||
|
.. _`Issue #327`: http://bitbucket.org/tarek/distribute/issue/327
|
||||||
|
.. _`Issue #328`: http://bitbucket.org/tarek/distribute/issue/328
|
||||||
|
.. _`Issue #329`: http://bitbucket.org/tarek/distribute/issue/329
|
||||||
|
.. _`Issue #334`: http://bitbucket.org/tarek/distribute/issue/334
|
||||||
|
.. _`Issue #335`: http://bitbucket.org/tarek/distribute/issue/335
|
||||||
|
.. _`Issue #336`: http://bitbucket.org/tarek/distribute/issue/336
|
||||||
|
.. _`Issue #341`: http://bitbucket.org/tarek/distribute/issue/341
|
||||||
|
.. _`Issue 100`: http://bitbucket.org/tarek/distribute/issue/100
|
||||||
|
.. _`Issue 101`: http://bitbucket.org/tarek/distribute/issue/101
|
||||||
|
.. _`Issue 103`: http://bitbucket.org/tarek/distribute/issue/103
|
||||||
|
.. _`Issue 104`: http://bitbucket.org/tarek/distribute/issue/104
|
||||||
|
.. _`Issue 108`: http://bitbucket.org/tarek/distribute/issue/108
|
||||||
|
.. _`Issue 11`: http://bitbucket.org/tarek/distribute/issue/11
|
||||||
|
.. _`Issue 112`: http://bitbucket.org/tarek/distribute/issue/112
|
||||||
|
.. _`Issue 121`: http://bitbucket.org/tarek/distribute/issue/121
|
||||||
|
.. _`Issue 133`: http://bitbucket.org/tarek/distribute/issue/133
|
||||||
|
.. _`Issue 134`: http://bitbucket.org/tarek/distribute/issue/134
|
||||||
|
.. _`Issue 138`: http://bitbucket.org/tarek/distribute/issue/138
|
||||||
|
.. _`Issue 143`: http://bitbucket.org/tarek/distribute/issue/143
|
||||||
|
.. _`Issue 146`: http://bitbucket.org/tarek/distribute/issue/146
|
||||||
|
.. _`Issue 147`: http://bitbucket.org/tarek/distribute/issue/147
|
||||||
|
.. _`Issue 149`: http://bitbucket.org/tarek/distribute/issue/149
|
||||||
|
.. _`Issue 15`: http://bitbucket.org/tarek/distribute/issue/15
|
||||||
|
.. _`Issue 150`: http://bitbucket.org/tarek/distribute/issue/150
|
||||||
|
.. _`Issue 160`: http://bitbucket.org/tarek/distribute/issue/160
|
||||||
|
.. _`Issue 163`: http://bitbucket.org/tarek/distribute/issue/163
|
||||||
|
.. _`Issue 170`: http://bitbucket.org/tarek/distribute/issue/170
|
||||||
|
.. _`Issue 171`: http://bitbucket.org/tarek/distribute/issue/171
|
||||||
|
.. _`Issue 174`: http://bitbucket.org/tarek/distribute/issue/174
|
||||||
|
.. _`Issue 192`: http://bitbucket.org/tarek/distribute/issue/192
|
||||||
|
.. _`Issue 193`: http://bitbucket.org/tarek/distribute/issue/193
|
||||||
|
.. _`Issue 195`: http://bitbucket.org/tarek/distribute/issue/195
|
||||||
|
.. _`Issue 196`: http://bitbucket.org/tarek/distribute/issue/196
|
||||||
|
.. _`Issue 200`: http://bitbucket.org/tarek/distribute/issue/200
|
||||||
|
.. _`Issue 204`: http://bitbucket.org/tarek/distribute/issue/204
|
||||||
|
.. _`Issue 205`: http://bitbucket.org/tarek/distribute/issue/205
|
||||||
|
.. _`Issue 206`: http://bitbucket.org/tarek/distribute/issue/206
|
||||||
|
.. _`Issue 21`: http://bitbucket.org/tarek/distribute/issue/21
|
||||||
|
.. _`Issue 210`: http://bitbucket.org/tarek/distribute/issue/210
|
||||||
|
.. _`Issue 58`: http://bitbucket.org/tarek/distribute/issue/58
|
||||||
|
.. _`Issue 64`: http://bitbucket.org/tarek/distribute/issue/64
|
||||||
|
.. _`Issue 65`: http://bitbucket.org/tarek/distribute/issue/65
|
||||||
|
.. _`Issue 67`: http://bitbucket.org/tarek/distribute/issue/67
|
||||||
|
.. _`Issue 70`: http://bitbucket.org/tarek/distribute/issue/70
|
||||||
|
.. _`Issue 72`: http://bitbucket.org/tarek/distribute/issue/72
|
||||||
|
.. _`Issue 74`: http://bitbucket.org/tarek/distribute/issue/74
|
||||||
|
.. _`Issue 80`: http://bitbucket.org/tarek/distribute/issue/80
|
||||||
|
.. _`Issue 86`: http://bitbucket.org/tarek/distribute/issue/86
|
||||||
|
.. _`Issue 87`: http://bitbucket.org/tarek/distribute/issue/87
|
||||||
|
.. _`Issue 89`: http://bitbucket.org/tarek/distribute/issue/89
|
||||||
|
.. _`Issue 90`: http://bitbucket.org/tarek/distribute/issue/90
|
||||||
|
.. _`Issue 92`: http://bitbucket.org/tarek/distribute/issue/92
|
||||||
|
.. _`Issue 93`: http://bitbucket.org/tarek/distribute/issue/93
|
||||||
|
.. _`Issue 99`: http://bitbucket.org/tarek/distribute/issue/99
|
||||||
|
.. _`issue
|
||||||
|
449`: http://bitbucket.org/tarek/distribute/issue/449
|
||||||
|
.. _`issue #1`: http://bitbucket.org/tarek/distribute/issue/1
|
||||||
|
.. _`issue #10`: http://bitbucket.org/tarek/distribute/issue/10
|
||||||
|
.. _`issue #12`: http://bitbucket.org/tarek/distribute/issue/12
|
||||||
|
.. _`issue #13`: http://bitbucket.org/tarek/distribute/issue/13
|
||||||
|
.. _`issue #16`: http://bitbucket.org/tarek/distribute/issue/16
|
||||||
|
.. _`issue #18`: http://bitbucket.org/tarek/distribute/issue/18
|
||||||
|
.. _`issue #3`: http://bitbucket.org/tarek/distribute/issue/3
|
||||||
|
.. _`issue #31`: http://bitbucket.org/tarek/distribute/issue/31
|
||||||
|
.. _`issue #40`: http://bitbucket.org/tarek/distribute/issue/40
|
||||||
|
.. _`issue #49`: http://bitbucket.org/tarek/distribute/issue/49
|
||||||
|
.. _`issue #5`: http://bitbucket.org/tarek/distribute/issue/5
|
||||||
|
.. _`issue #50`: http://bitbucket.org/tarek/distribute/issue/50
|
||||||
|
.. _`issue #52`: http://bitbucket.org/tarek/distribute/issue/52
|
||||||
|
.. _`issue #56`: http://bitbucket.org/tarek/distribute/issue/56
|
||||||
|
.. _`issue #6`: http://bitbucket.org/tarek/distribute/issue/6
|
||||||
|
.. _`issue #7`: http://bitbucket.org/tarek/distribute/issue/7
|
||||||
|
.. _`issue #8`: http://bitbucket.org/tarek/distribute/issue/8
|
||||||
|
.. _`issue #9`: http://bitbucket.org/tarek/distribute/issue/9
|
||||||
|
.. _`issue1980`: http://bitbucket.org/tarek/distribute/issue/1980
|
||||||
|
.. _`issue1981`: http://bitbucket.org/tarek/distribute/issue/1981
|
||||||
|
.. _`issue2`: http://bitbucket.org/tarek/distribute/issue/2
|
||||||
|
.. _`issue20`: http://bitbucket.org/tarek/distribute/issue/20
|
||||||
|
.. _`issue33`: http://bitbucket.org/tarek/distribute/issue/33
|
||||||
|
.. _`issue39`: http://bitbucket.org/tarek/distribute/issue/39
|
||||||
|
.. _`issue40`: http://bitbucket.org/tarek/distribute/issue/40
|
||||||
|
.. _`issue41`: http://bitbucket.org/tarek/distribute/issue/41
|
||||||
|
.. _`issue44`: http://bitbucket.org/tarek/distribute/issue/44
|
||||||
|
|
||||||
|
|
||||||
|
Keywords: CPAN PyPI distutils eggs package management
|
||||||
|
Platform: UNKNOWN
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: License :: OSI Approved :: Python Software Foundation License
|
||||||
|
Classifier: License :: OSI Approved :: Zope Public License
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Programming Language :: Python :: 2.4
|
||||||
|
Classifier: Programming Language :: Python :: 2.5
|
||||||
|
Classifier: Programming Language :: Python :: 2.6
|
||||||
|
Classifier: Programming Language :: Python :: 2.7
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3.1
|
||||||
|
Classifier: Programming Language :: Python :: 3.2
|
||||||
|
Classifier: Programming Language :: Python :: 3.3
|
||||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||||
|
Classifier: Topic :: System :: Archiving :: Packaging
|
||||||
|
Classifier: Topic :: System :: Systems Administration
|
||||||
|
Classifier: Topic :: Utilities
|
||||||
Vendored
+228
@@ -0,0 +1,228 @@
|
|||||||
|
===============================
|
||||||
|
Installing and Using Distribute
|
||||||
|
===============================
|
||||||
|
|
||||||
|
.. contents:: **Table of Contents**
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Disclaimers
|
||||||
|
-----------
|
||||||
|
|
||||||
|
About the fork
|
||||||
|
==============
|
||||||
|
|
||||||
|
`Distribute` is a fork of the `Setuptools` project.
|
||||||
|
|
||||||
|
Distribute is intended to replace Setuptools as the standard method
|
||||||
|
for working with Python module distributions.
|
||||||
|
|
||||||
|
The fork has two goals:
|
||||||
|
|
||||||
|
- Providing a backward compatible version to replace Setuptools
|
||||||
|
and make all distributions that depend on Setuptools work as
|
||||||
|
before, but with less bugs and behaviorial issues.
|
||||||
|
|
||||||
|
This work is done in the 0.6.x series.
|
||||||
|
|
||||||
|
Starting with version 0.6.2, Distribute supports Python 3.
|
||||||
|
Installing and using distribute for Python 3 code works exactly
|
||||||
|
the same as for Python 2 code, but Distribute also helps you to support
|
||||||
|
Python 2 and Python 3 from the same source code by letting you run 2to3
|
||||||
|
on the code as a part of the build process, by setting the keyword parameter
|
||||||
|
``use_2to3`` to True. See http://packages.python.org/distribute for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
- Refactoring the code, and releasing it in several distributions.
|
||||||
|
This work is being done in the 0.7.x series but not yet released.
|
||||||
|
|
||||||
|
The roadmap is still evolving, and the page that is up-to-date is
|
||||||
|
located at : `http://packages.python.org/distribute/roadmap`.
|
||||||
|
|
||||||
|
If you install `Distribute` and want to switch back for any reason to
|
||||||
|
`Setuptools`, get to the `Uninstallation instructions`_ section.
|
||||||
|
|
||||||
|
More documentation
|
||||||
|
==================
|
||||||
|
|
||||||
|
You can get more information in the Sphinx-based documentation, located
|
||||||
|
at http://packages.python.org/distribute. This documentation includes the old
|
||||||
|
Setuptools documentation that is slowly replaced, and brand new content.
|
||||||
|
|
||||||
|
About the installation process
|
||||||
|
==============================
|
||||||
|
|
||||||
|
The `Distribute` installer modifies your installation by de-activating an
|
||||||
|
existing installation of `Setuptools` in a bootstrap process. This process
|
||||||
|
has been tested in various installation schemes and contexts but in case of a
|
||||||
|
bug during this process your Python installation might be left in a broken
|
||||||
|
state. Since all modified files and directories are copied before the
|
||||||
|
installation starts, you will be able to get back to a normal state by reading
|
||||||
|
the instructions in the `Uninstallation instructions`_ section.
|
||||||
|
|
||||||
|
In any case, it is recommended to save you `site-packages` directory before
|
||||||
|
you start the installation of `Distribute`.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
Installation Instructions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Distribute is only released as a source distribution.
|
||||||
|
|
||||||
|
It can be installed using pip, and can be done so with the source tarball,
|
||||||
|
or by using the ``distribute_setup.py`` script provided online.
|
||||||
|
|
||||||
|
``distribute_setup.py`` is the simplest and preferred way on all systems.
|
||||||
|
|
||||||
|
distribute_setup.py
|
||||||
|
===================
|
||||||
|
|
||||||
|
Download
|
||||||
|
`distribute_setup.py <http://python-distribute.org/distribute_setup.py>`_
|
||||||
|
and execute it, using the Python interpreter of your choice.
|
||||||
|
|
||||||
|
If your shell has the ``curl`` program you can do::
|
||||||
|
|
||||||
|
$ curl -O http://python-distribute.org/distribute_setup.py
|
||||||
|
$ python distribute_setup.py
|
||||||
|
|
||||||
|
Notice this file is also provided in the source release.
|
||||||
|
|
||||||
|
pip
|
||||||
|
===
|
||||||
|
|
||||||
|
Run easy_install or pip::
|
||||||
|
|
||||||
|
$ pip install distribute
|
||||||
|
|
||||||
|
Source installation
|
||||||
|
===================
|
||||||
|
|
||||||
|
Download the source tarball, uncompress it, then run the install command::
|
||||||
|
|
||||||
|
$ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.34.tar.gz
|
||||||
|
$ tar -xzvf distribute-0.6.34.tar.gz
|
||||||
|
$ cd distribute-0.6.34
|
||||||
|
$ python setup.py install
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
Uninstallation Instructions
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Like other distutils-based distributions, Distribute doesn't provide an
|
||||||
|
uninstaller yet. It's all done manually! We are all waiting for PEP 376
|
||||||
|
support in Python.
|
||||||
|
|
||||||
|
Distribute is installed in three steps:
|
||||||
|
|
||||||
|
1. it gets out of the way an existing installation of Setuptools
|
||||||
|
2. it installs a `fake` setuptools installation
|
||||||
|
3. it installs distribute
|
||||||
|
|
||||||
|
Distribute can be removed like this:
|
||||||
|
|
||||||
|
- remove the ``distribute*.egg`` file located in your site-packages directory
|
||||||
|
- remove the ``setuptools.pth`` file located in you site-packages directory
|
||||||
|
- remove the easy_install script located in you ``sys.prefix/bin`` directory
|
||||||
|
- remove the ``setuptools*.egg`` directory located in your site-packages directory,
|
||||||
|
if any.
|
||||||
|
|
||||||
|
If you want to get back to setuptools:
|
||||||
|
|
||||||
|
- reinstall setuptools using its instruction.
|
||||||
|
|
||||||
|
Lastly:
|
||||||
|
|
||||||
|
- remove the *.OLD.* directory located in your site-packages directory if any,
|
||||||
|
**once you have checked everything was working correctly again**.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
Quick help for developers
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
To create an egg which is compatible with Distribute, use the same
|
||||||
|
practice as with Setuptools, e.g.::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
)
|
||||||
|
|
||||||
|
To use `pkg_resources` to access data files in the egg, you should
|
||||||
|
require the Setuptools distribution explicitly::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
install_requires=['setuptools']
|
||||||
|
)
|
||||||
|
|
||||||
|
Only if you need Distribute-specific functionality should you depend
|
||||||
|
on it explicitly. In this case, replace the Setuptools dependency::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
install_requires=['distribute']
|
||||||
|
)
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Install FAQ
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- **Why is Distribute wrapping my Setuptools installation?**
|
||||||
|
|
||||||
|
Since Distribute is a fork, and since it provides the same package
|
||||||
|
and modules, it renames the existing Setuptools egg and inserts a
|
||||||
|
new one which merely wraps the Distribute code. This way, full
|
||||||
|
backwards compatibility is kept for packages which rely on the
|
||||||
|
Setuptools modules.
|
||||||
|
|
||||||
|
At the same time, packages can meet their dependency on Setuptools
|
||||||
|
without actually installing it (which would disable Distribute).
|
||||||
|
|
||||||
|
- **How does Distribute interact with virtualenv?**
|
||||||
|
|
||||||
|
Everytime you create a virtualenv it will install setuptools by default.
|
||||||
|
You either need to re-install Distribute in it right after or pass the
|
||||||
|
``--distribute`` option when creating it.
|
||||||
|
|
||||||
|
Once installed, your virtualenv will use Distribute transparently.
|
||||||
|
|
||||||
|
Although, if you have Setuptools installed in your system-wide Python,
|
||||||
|
and if the virtualenv you are in was generated without the `--no-site-packages`
|
||||||
|
option, the Distribute installation will stop.
|
||||||
|
|
||||||
|
You need in this case to build a virtualenv with the `--no-site-packages`
|
||||||
|
option or to install `Distribute` globally.
|
||||||
|
|
||||||
|
- **How does Distribute interacts with zc.buildout?**
|
||||||
|
|
||||||
|
You can use Distribute in your zc.buildout, with the --distribute option,
|
||||||
|
starting at zc.buildout 1.4.2::
|
||||||
|
|
||||||
|
$ python bootstrap.py --distribute
|
||||||
|
|
||||||
|
For previous zc.buildout versions, *the only thing* you need to do
|
||||||
|
is use the bootstrap at `http://python-distribute.org/bootstrap.py`. Run
|
||||||
|
that bootstrap and ``bin/buildout`` (and all other buildout-generated
|
||||||
|
scripts) will transparently use distribute instead of setuptools. You do
|
||||||
|
not need a specific buildout release.
|
||||||
|
|
||||||
|
A shared eggs directory is no problem (since 0.6.6): the setuptools egg is
|
||||||
|
left in place unmodified. So other buildouts that do not yet use the new
|
||||||
|
bootstrap continue to work just fine. And there is no need to list
|
||||||
|
``distribute`` somewhere in your eggs: using the bootstrap is enough.
|
||||||
|
|
||||||
|
The source code for the bootstrap script is located at
|
||||||
|
`http://bitbucket.org/tarek/buildout-distribute`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------
|
||||||
|
Feedback and getting involved
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Mailing list: http://mail.python.org/mailman/listinfo/distutils-sig
|
||||||
|
- Issue tracker: http://bitbucket.org/tarek/distribute/issues/
|
||||||
|
- Code Repository: http://bitbucket.org/tarek/distribute
|
||||||
|
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
try:
|
||||||
|
import ast
|
||||||
|
from _markerlib.markers import default_environment, compile, interpret
|
||||||
|
except ImportError:
|
||||||
|
if 'ast' in globals():
|
||||||
|
raise
|
||||||
|
def default_environment():
|
||||||
|
return {}
|
||||||
|
def compile(marker):
|
||||||
|
def marker_fn(environment=None, override=None):
|
||||||
|
# 'empty markers are True' heuristic won't install extra deps.
|
||||||
|
return not marker.strip()
|
||||||
|
marker_fn.__doc__ = marker
|
||||||
|
return marker_fn
|
||||||
|
def interpret(marker, environment=None, override=None):
|
||||||
|
return compile(marker)()
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Interpret PEP 345 environment markers.
|
||||||
|
|
||||||
|
EXPR [in|==|!=|not in] EXPR [or|and] ...
|
||||||
|
|
||||||
|
where EXPR belongs to any of those:
|
||||||
|
|
||||||
|
python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1])
|
||||||
|
python_full_version = sys.version.split()[0]
|
||||||
|
os.name = os.name
|
||||||
|
sys.platform = sys.platform
|
||||||
|
platform.version = platform.version()
|
||||||
|
platform.machine = platform.machine()
|
||||||
|
platform.python_implementation = platform.python_implementation()
|
||||||
|
a free string, like '2.6', or 'win32'
|
||||||
|
"""
|
||||||
|
|
||||||
|
__all__ = ['default_environment', 'compile', 'interpret']
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
import weakref
|
||||||
|
|
||||||
|
_builtin_compile = compile
|
||||||
|
|
||||||
|
try:
|
||||||
|
from platform import python_implementation
|
||||||
|
except ImportError:
|
||||||
|
if os.name == "java":
|
||||||
|
# Jython 2.5 has ast module, but not platform.python_implementation() function.
|
||||||
|
def python_implementation():
|
||||||
|
return "Jython"
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
# restricted set of variables
|
||||||
|
_VARS = {'sys.platform': sys.platform,
|
||||||
|
'python_version': '%s.%s' % sys.version_info[:2],
|
||||||
|
# FIXME parsing sys.platform is not reliable, but there is no other
|
||||||
|
# way to get e.g. 2.7.2+, and the PEP is defined with sys.version
|
||||||
|
'python_full_version': sys.version.split(' ', 1)[0],
|
||||||
|
'os.name': os.name,
|
||||||
|
'platform.version': platform.version(),
|
||||||
|
'platform.machine': platform.machine(),
|
||||||
|
'platform.python_implementation': python_implementation(),
|
||||||
|
'extra': None # wheel extension
|
||||||
|
}
|
||||||
|
|
||||||
|
def default_environment():
|
||||||
|
"""Return copy of default PEP 385 globals dictionary."""
|
||||||
|
return dict(_VARS)
|
||||||
|
|
||||||
|
class ASTWhitelist(ast.NodeTransformer):
|
||||||
|
def __init__(self, statement):
|
||||||
|
self.statement = statement # for error messages
|
||||||
|
|
||||||
|
ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str)
|
||||||
|
# Bool operations
|
||||||
|
ALLOWED += (ast.And, ast.Or)
|
||||||
|
# Comparison operations
|
||||||
|
ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn)
|
||||||
|
|
||||||
|
def visit(self, node):
|
||||||
|
"""Ensure statement only contains allowed nodes."""
|
||||||
|
if not isinstance(node, self.ALLOWED):
|
||||||
|
raise SyntaxError('Not allowed in environment markers.\n%s\n%s' %
|
||||||
|
(self.statement,
|
||||||
|
(' ' * node.col_offset) + '^'))
|
||||||
|
return ast.NodeTransformer.visit(self, node)
|
||||||
|
|
||||||
|
def visit_Attribute(self, node):
|
||||||
|
"""Flatten one level of attribute access."""
|
||||||
|
new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx)
|
||||||
|
return ast.copy_location(new_node, node)
|
||||||
|
|
||||||
|
def parse_marker(marker):
|
||||||
|
tree = ast.parse(marker, mode='eval')
|
||||||
|
new_tree = ASTWhitelist(marker).generic_visit(tree)
|
||||||
|
return new_tree
|
||||||
|
|
||||||
|
def compile_marker(parsed_marker):
|
||||||
|
return _builtin_compile(parsed_marker, '<environment marker>', 'eval',
|
||||||
|
dont_inherit=True)
|
||||||
|
|
||||||
|
_cache = weakref.WeakValueDictionary()
|
||||||
|
|
||||||
|
def compile(marker):
|
||||||
|
"""Return compiled marker as a function accepting an environment dict."""
|
||||||
|
try:
|
||||||
|
return _cache[marker]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
if not marker.strip():
|
||||||
|
def marker_fn(environment=None, override=None):
|
||||||
|
""""""
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
compiled_marker = compile_marker(parse_marker(marker))
|
||||||
|
def marker_fn(environment=None, override=None):
|
||||||
|
"""override updates environment"""
|
||||||
|
if override is None:
|
||||||
|
override = {}
|
||||||
|
if environment is None:
|
||||||
|
environment = default_environment()
|
||||||
|
environment.update(override)
|
||||||
|
return eval(compiled_marker, environment)
|
||||||
|
marker_fn.__doc__ = marker
|
||||||
|
_cache[marker] = marker_fn
|
||||||
|
return _cache[marker]
|
||||||
|
|
||||||
|
def interpret(marker, environment=None):
|
||||||
|
return compile(marker)(environment)
|
||||||
@@ -0,0 +1,868 @@
|
|||||||
|
Metadata-Version: 1.1
|
||||||
|
Name: distribute
|
||||||
|
Version: 0.6.34
|
||||||
|
Summary: Easily download, build, install, upgrade, and uninstall Python packages
|
||||||
|
Home-page: http://packages.python.org/distribute
|
||||||
|
Author: The fellowship of the packaging
|
||||||
|
Author-email: distutils-sig@python.org
|
||||||
|
License: PSF or ZPL
|
||||||
|
Description: ===============================
|
||||||
|
Installing and Using Distribute
|
||||||
|
===============================
|
||||||
|
|
||||||
|
.. contents:: **Table of Contents**
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Disclaimers
|
||||||
|
-----------
|
||||||
|
|
||||||
|
About the fork
|
||||||
|
==============
|
||||||
|
|
||||||
|
`Distribute` is a fork of the `Setuptools` project.
|
||||||
|
|
||||||
|
Distribute is intended to replace Setuptools as the standard method
|
||||||
|
for working with Python module distributions.
|
||||||
|
|
||||||
|
The fork has two goals:
|
||||||
|
|
||||||
|
- Providing a backward compatible version to replace Setuptools
|
||||||
|
and make all distributions that depend on Setuptools work as
|
||||||
|
before, but with less bugs and behaviorial issues.
|
||||||
|
|
||||||
|
This work is done in the 0.6.x series.
|
||||||
|
|
||||||
|
Starting with version 0.6.2, Distribute supports Python 3.
|
||||||
|
Installing and using distribute for Python 3 code works exactly
|
||||||
|
the same as for Python 2 code, but Distribute also helps you to support
|
||||||
|
Python 2 and Python 3 from the same source code by letting you run 2to3
|
||||||
|
on the code as a part of the build process, by setting the keyword parameter
|
||||||
|
``use_2to3`` to True. See http://packages.python.org/distribute for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
- Refactoring the code, and releasing it in several distributions.
|
||||||
|
This work is being done in the 0.7.x series but not yet released.
|
||||||
|
|
||||||
|
The roadmap is still evolving, and the page that is up-to-date is
|
||||||
|
located at : `http://packages.python.org/distribute/roadmap`.
|
||||||
|
|
||||||
|
If you install `Distribute` and want to switch back for any reason to
|
||||||
|
`Setuptools`, get to the `Uninstallation instructions`_ section.
|
||||||
|
|
||||||
|
More documentation
|
||||||
|
==================
|
||||||
|
|
||||||
|
You can get more information in the Sphinx-based documentation, located
|
||||||
|
at http://packages.python.org/distribute. This documentation includes the old
|
||||||
|
Setuptools documentation that is slowly replaced, and brand new content.
|
||||||
|
|
||||||
|
About the installation process
|
||||||
|
==============================
|
||||||
|
|
||||||
|
The `Distribute` installer modifies your installation by de-activating an
|
||||||
|
existing installation of `Setuptools` in a bootstrap process. This process
|
||||||
|
has been tested in various installation schemes and contexts but in case of a
|
||||||
|
bug during this process your Python installation might be left in a broken
|
||||||
|
state. Since all modified files and directories are copied before the
|
||||||
|
installation starts, you will be able to get back to a normal state by reading
|
||||||
|
the instructions in the `Uninstallation instructions`_ section.
|
||||||
|
|
||||||
|
In any case, it is recommended to save you `site-packages` directory before
|
||||||
|
you start the installation of `Distribute`.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
Installation Instructions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Distribute is only released as a source distribution.
|
||||||
|
|
||||||
|
It can be installed using pip, and can be done so with the source tarball,
|
||||||
|
or by using the ``distribute_setup.py`` script provided online.
|
||||||
|
|
||||||
|
``distribute_setup.py`` is the simplest and preferred way on all systems.
|
||||||
|
|
||||||
|
distribute_setup.py
|
||||||
|
===================
|
||||||
|
|
||||||
|
Download
|
||||||
|
`distribute_setup.py <http://python-distribute.org/distribute_setup.py>`_
|
||||||
|
and execute it, using the Python interpreter of your choice.
|
||||||
|
|
||||||
|
If your shell has the ``curl`` program you can do::
|
||||||
|
|
||||||
|
$ curl -O http://python-distribute.org/distribute_setup.py
|
||||||
|
$ python distribute_setup.py
|
||||||
|
|
||||||
|
Notice this file is also provided in the source release.
|
||||||
|
|
||||||
|
pip
|
||||||
|
===
|
||||||
|
|
||||||
|
Run easy_install or pip::
|
||||||
|
|
||||||
|
$ pip install distribute
|
||||||
|
|
||||||
|
Source installation
|
||||||
|
===================
|
||||||
|
|
||||||
|
Download the source tarball, uncompress it, then run the install command::
|
||||||
|
|
||||||
|
$ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.34.tar.gz
|
||||||
|
$ tar -xzvf distribute-0.6.34.tar.gz
|
||||||
|
$ cd distribute-0.6.34
|
||||||
|
$ python setup.py install
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
Uninstallation Instructions
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Like other distutils-based distributions, Distribute doesn't provide an
|
||||||
|
uninstaller yet. It's all done manually! We are all waiting for PEP 376
|
||||||
|
support in Python.
|
||||||
|
|
||||||
|
Distribute is installed in three steps:
|
||||||
|
|
||||||
|
1. it gets out of the way an existing installation of Setuptools
|
||||||
|
2. it installs a `fake` setuptools installation
|
||||||
|
3. it installs distribute
|
||||||
|
|
||||||
|
Distribute can be removed like this:
|
||||||
|
|
||||||
|
- remove the ``distribute*.egg`` file located in your site-packages directory
|
||||||
|
- remove the ``setuptools.pth`` file located in you site-packages directory
|
||||||
|
- remove the easy_install script located in you ``sys.prefix/bin`` directory
|
||||||
|
- remove the ``setuptools*.egg`` directory located in your site-packages directory,
|
||||||
|
if any.
|
||||||
|
|
||||||
|
If you want to get back to setuptools:
|
||||||
|
|
||||||
|
- reinstall setuptools using its instruction.
|
||||||
|
|
||||||
|
Lastly:
|
||||||
|
|
||||||
|
- remove the *.OLD.* directory located in your site-packages directory if any,
|
||||||
|
**once you have checked everything was working correctly again**.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
Quick help for developers
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
To create an egg which is compatible with Distribute, use the same
|
||||||
|
practice as with Setuptools, e.g.::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
)
|
||||||
|
|
||||||
|
To use `pkg_resources` to access data files in the egg, you should
|
||||||
|
require the Setuptools distribution explicitly::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
install_requires=['setuptools']
|
||||||
|
)
|
||||||
|
|
||||||
|
Only if you need Distribute-specific functionality should you depend
|
||||||
|
on it explicitly. In this case, replace the Setuptools dependency::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
install_requires=['distribute']
|
||||||
|
)
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Install FAQ
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- **Why is Distribute wrapping my Setuptools installation?**
|
||||||
|
|
||||||
|
Since Distribute is a fork, and since it provides the same package
|
||||||
|
and modules, it renames the existing Setuptools egg and inserts a
|
||||||
|
new one which merely wraps the Distribute code. This way, full
|
||||||
|
backwards compatibility is kept for packages which rely on the
|
||||||
|
Setuptools modules.
|
||||||
|
|
||||||
|
At the same time, packages can meet their dependency on Setuptools
|
||||||
|
without actually installing it (which would disable Distribute).
|
||||||
|
|
||||||
|
- **How does Distribute interact with virtualenv?**
|
||||||
|
|
||||||
|
Everytime you create a virtualenv it will install setuptools by default.
|
||||||
|
You either need to re-install Distribute in it right after or pass the
|
||||||
|
``--distribute`` option when creating it.
|
||||||
|
|
||||||
|
Once installed, your virtualenv will use Distribute transparently.
|
||||||
|
|
||||||
|
Although, if you have Setuptools installed in your system-wide Python,
|
||||||
|
and if the virtualenv you are in was generated without the `--no-site-packages`
|
||||||
|
option, the Distribute installation will stop.
|
||||||
|
|
||||||
|
You need in this case to build a virtualenv with the `--no-site-packages`
|
||||||
|
option or to install `Distribute` globally.
|
||||||
|
|
||||||
|
- **How does Distribute interacts with zc.buildout?**
|
||||||
|
|
||||||
|
You can use Distribute in your zc.buildout, with the --distribute option,
|
||||||
|
starting at zc.buildout 1.4.2::
|
||||||
|
|
||||||
|
$ python bootstrap.py --distribute
|
||||||
|
|
||||||
|
For previous zc.buildout versions, *the only thing* you need to do
|
||||||
|
is use the bootstrap at `http://python-distribute.org/bootstrap.py`. Run
|
||||||
|
that bootstrap and ``bin/buildout`` (and all other buildout-generated
|
||||||
|
scripts) will transparently use distribute instead of setuptools. You do
|
||||||
|
not need a specific buildout release.
|
||||||
|
|
||||||
|
A shared eggs directory is no problem (since 0.6.6): the setuptools egg is
|
||||||
|
left in place unmodified. So other buildouts that do not yet use the new
|
||||||
|
bootstrap continue to work just fine. And there is no need to list
|
||||||
|
``distribute`` somewhere in your eggs: using the bootstrap is enough.
|
||||||
|
|
||||||
|
The source code for the bootstrap script is located at
|
||||||
|
`http://bitbucket.org/tarek/buildout-distribute`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------
|
||||||
|
Feedback and getting involved
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Mailing list: http://mail.python.org/mailman/listinfo/distutils-sig
|
||||||
|
- Issue tracker: http://bitbucket.org/tarek/distribute/issues/
|
||||||
|
- Code Repository: http://bitbucket.org/tarek/distribute
|
||||||
|
|
||||||
|
=======
|
||||||
|
CHANGES
|
||||||
|
=======
|
||||||
|
|
||||||
|
----------
|
||||||
|
Unreleased
|
||||||
|
----------
|
||||||
|
|
||||||
|
+ `Issue #341`_: 0.6.33 fails to build under python 2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.33
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fix 2 errors with Jython 2.5.
|
||||||
|
* Fix 1 failure with Jython 2.5 and 2.7.
|
||||||
|
* Disable workaround for Jython scripts on Linux systems.
|
||||||
|
* `Issue #336`_: `setup.py` no longer masks failure exit code when tests fail.
|
||||||
|
* Fix issue in pkg_resources where try/except around a platform-dependent
|
||||||
|
import would trigger hook load failures on Mercurial. See pull request 32
|
||||||
|
for details.
|
||||||
|
* `Issue #341`_: Fix a ResourceWarning.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.32
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fix test suite with Python 2.6.
|
||||||
|
* Fix some DeprecationWarnings and ResourceWarnings.
|
||||||
|
* `Issue #335`_: Backed out `setup_requires` superceding installed requirements
|
||||||
|
until regression can be addressed.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.31
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #303`_: Make sure the manifest only ever contains UTF-8 in Python 3.
|
||||||
|
* `Issue #329`_: Properly close files created by tests for compatibility with
|
||||||
|
Jython.
|
||||||
|
* Work around Jython bugs `#1980 <http://bugs.jython.org/`issue1980`_>`_ and
|
||||||
|
`#1981 <http://bugs.jython.org/`issue1981`_>`_.
|
||||||
|
* `Issue #334`_: Provide workaround for packages that reference `sys.__stdout__`
|
||||||
|
such as numpy does. This change should address
|
||||||
|
`virtualenv #359 <https://github.com/pypa/virtualenv/issues/359>`_ as long
|
||||||
|
as the system encoding is UTF-8 or the IO encoding is specified in the
|
||||||
|
environment, i.e.::
|
||||||
|
|
||||||
|
PYTHONIOENCODING=utf8 pip install numpy
|
||||||
|
|
||||||
|
* Fix for encoding issue when installing from Windows executable on Python 3.
|
||||||
|
* `Issue #323`_: Allow `setup_requires` requirements to supercede installed
|
||||||
|
requirements. Added some new keyword arguments to existing pkg_resources
|
||||||
|
methods. Also had to updated how __path__ is handled for namespace packages
|
||||||
|
to ensure that when a new egg distribution containing a namespace package is
|
||||||
|
placed on sys.path, the entries in __path__ are found in the same order they
|
||||||
|
would have been in had that egg been on the path when pkg_resources was
|
||||||
|
first imported.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.30
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #328`_: Clean up temporary directories in distribute_setup.py.
|
||||||
|
* Fix fatal bug in distribute_setup.py.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.29
|
||||||
|
------
|
||||||
|
|
||||||
|
* Pull Request #14: Honor file permissions in zip files.
|
||||||
|
* `Issue #327`_: Merged pull request #24 to fix a dependency problem with pip.
|
||||||
|
* Merged pull request #23 to fix https://github.com/pypa/virtualenv/issues/301.
|
||||||
|
* If Sphinx is installed, the `upload_docs` command now runs `build_sphinx`
|
||||||
|
to produce uploadable documentation.
|
||||||
|
* `Issue #326`_: `upload_docs` provided mangled auth credentials under Python 3.
|
||||||
|
* `Issue #320`_: Fix check for "createable" in distribute_setup.py.
|
||||||
|
* `Issue #305`_: Remove a warning that was triggered during normal operations.
|
||||||
|
* `Issue #311`_: Print metadata in UTF-8 independent of platform.
|
||||||
|
* `Issue #303`_: Read manifest file with UTF-8 encoding under Python 3.
|
||||||
|
* `Issue #301`_: Allow to run tests of namespace packages when using 2to3.
|
||||||
|
* `Issue #304`_: Prevent import loop in site.py under Python 3.3.
|
||||||
|
* `Issue #283`_: Reenable scanning of `*.pyc` / `*.pyo` files on Python 3.3.
|
||||||
|
* `Issue #299`_: The develop command didn't work on Python 3, when using 2to3,
|
||||||
|
as the egg link would go to the Python 2 source. Linking to the 2to3'd code
|
||||||
|
in build/lib makes it work, although you will have to rebuild the module
|
||||||
|
before testing it.
|
||||||
|
* `Issue #306`_: Even if 2to3 is used, we build in-place under Python 2.
|
||||||
|
* `Issue #307`_: Prints the full path when .svn/entries is broken.
|
||||||
|
* `Issue #313`_: Support for sdist subcommands (Python 2.7)
|
||||||
|
* `Issue #314`_: test_local_index() would fail an OS X.
|
||||||
|
* `Issue #310`_: Non-ascii characters in a namespace __init__.py causes errors.
|
||||||
|
* `Issue #218`_: Improved documentation on behavior of `package_data` and
|
||||||
|
`include_package_data`. Files indicated by `package_data` are now included
|
||||||
|
in the manifest.
|
||||||
|
* `distribute_setup.py` now allows a `--download-base` argument for retrieving
|
||||||
|
distribute from a specified location.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.28
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #294`_: setup.py can now be invoked from any directory.
|
||||||
|
* Scripts are now installed honoring the umask.
|
||||||
|
* Added support for .dist-info directories.
|
||||||
|
* `Issue #283`_: Fix and disable scanning of `*.pyc` / `*.pyo` files on
|
||||||
|
Python 3.3.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.27
|
||||||
|
------
|
||||||
|
|
||||||
|
* Support current snapshots of CPython 3.3.
|
||||||
|
* Distribute now recognizes README.rst as a standard, default readme file.
|
||||||
|
* Exclude 'encodings' modules when removing modules from sys.modules.
|
||||||
|
Workaround for #285.
|
||||||
|
* `Issue #231`_: Don't fiddle with system python when used with buildout
|
||||||
|
(bootstrap.py)
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.26
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #183`_: Symlinked files are now extracted from source distributions.
|
||||||
|
* `Issue #227`_: Easy_install fetch parameters are now passed during the
|
||||||
|
installation of a source distribution; now fulfillment of setup_requires
|
||||||
|
dependencies will honor the parameters passed to easy_install.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.25
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #258`_: Workaround a cache issue
|
||||||
|
* `Issue #260`_: distribute_setup.py now accepts the --user parameter for
|
||||||
|
Python 2.6 and later.
|
||||||
|
* `Issue #262`_: package_index.open_with_auth no longer throws LookupError
|
||||||
|
on Python 3.
|
||||||
|
* `Issue #269`_: AttributeError when an exception occurs reading Manifest.in
|
||||||
|
on late releases of Python.
|
||||||
|
* `Issue #272`_: Prevent TypeError when namespace package names are unicode
|
||||||
|
and single-install-externally-managed is used. Also fixes PIP `issue
|
||||||
|
449`_.
|
||||||
|
* `Issue #273`_: Legacy script launchers now install with Python2/3 support.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.24
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #249`_: Added options to exclude 2to3 fixers
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.23
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #244`_: Fixed a test
|
||||||
|
* `Issue #243`_: Fixed a test
|
||||||
|
* `Issue #239`_: Fixed a test
|
||||||
|
* `Issue #240`_: Fixed a test
|
||||||
|
* `Issue #241`_: Fixed a test
|
||||||
|
* `Issue #237`_: Fixed a test
|
||||||
|
* `Issue #238`_: easy_install now uses 64bit executable wrappers on 64bit Python
|
||||||
|
* `Issue #208`_: Fixed parsed_versions, it now honors post-releases as noted in the documentation
|
||||||
|
* `Issue #207`_: Windows cli and gui wrappers pass CTRL-C to child python process
|
||||||
|
* `Issue #227`_: easy_install now passes its arguments to setup.py bdist_egg
|
||||||
|
* `Issue #225`_: Fixed a NameError on Python 2.5, 2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.21
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #225`_: FIxed a regression on py2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.20
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue #135`_: Include url in warning when processing URLs in package_index.
|
||||||
|
* `Issue #212`_: Fix issue where easy_instal fails on Python 3 on windows installer.
|
||||||
|
* `Issue #213`_: Fix typo in documentation.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.19
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 206`_: AttributeError: 'HTTPMessage' object has no attribute 'getheaders'
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.18
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 210`_: Fixed a regression introduced by `Issue 204`_ fix.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.17
|
||||||
|
------
|
||||||
|
|
||||||
|
* Support 'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT' environment
|
||||||
|
variable to allow to disable installation of easy_install-${version} script.
|
||||||
|
* Support Python >=3.1.4 and >=3.2.1.
|
||||||
|
* `Issue 204`_: Don't try to import the parent of a namespace package in
|
||||||
|
declare_namespace
|
||||||
|
* `Issue 196`_: Tolerate responses with multiple Content-Length headers
|
||||||
|
* `Issue 205`_: Sandboxing doesn't preserve working_set. Leads to setup_requires
|
||||||
|
problems.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.16
|
||||||
|
------
|
||||||
|
|
||||||
|
* Builds sdist gztar even on Windows (avoiding `Issue 193`_).
|
||||||
|
* `Issue 192`_: Fixed metadata omitted on Windows when package_dir
|
||||||
|
specified with forward-slash.
|
||||||
|
* `Issue 195`_: Cython build support.
|
||||||
|
* `Issue 200`_: Issues with recognizing 64-bit packages on Windows.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.15
|
||||||
|
------
|
||||||
|
|
||||||
|
* Fixed typo in bdist_egg
|
||||||
|
* Several issues under Python 3 has been solved.
|
||||||
|
* `Issue 146`_: Fixed missing DLL files after easy_install of windows exe package.
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.14
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 170`_: Fixed unittest failure. Thanks to Toshio.
|
||||||
|
* `Issue 171`_: Fixed race condition in unittests cause deadlocks in test suite.
|
||||||
|
* `Issue 143`_: Fixed a lookup issue with easy_install.
|
||||||
|
Thanks to David and Zooko.
|
||||||
|
* `Issue 174`_: Fixed the edit mode when its used with setuptools itself
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.13
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 160`_: 2.7 gives ValueError("Invalid IPv6 URL")
|
||||||
|
* `Issue 150`_: Fixed using ~/.local even in a --no-site-packages virtualenv
|
||||||
|
* `Issue 163`_: scan index links before external links, and don't use the md5 when
|
||||||
|
comparing two distributions
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.12
|
||||||
|
------
|
||||||
|
|
||||||
|
* `Issue 149`_: Fixed various failures on 2.3/2.4
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.11
|
||||||
|
------
|
||||||
|
|
||||||
|
* Found another case of SandboxViolation - fixed
|
||||||
|
* `Issue 15`_ and 48: Introduced a socket timeout of 15 seconds on url openings
|
||||||
|
* Added indexsidebar.html into MANIFEST.in
|
||||||
|
* `Issue 108`_: Fixed TypeError with Python3.1
|
||||||
|
* `Issue 121`_: Fixed --help install command trying to actually install.
|
||||||
|
* `Issue 112`_: Added an os.makedirs so that Tarek's solution will work.
|
||||||
|
* `Issue 133`_: Added --no-find-links to easy_install
|
||||||
|
* Added easy_install --user
|
||||||
|
* `Issue 100`_: Fixed develop --user not taking '.' in PYTHONPATH into account
|
||||||
|
* `Issue 134`_: removed spurious UserWarnings. Patch by VanLindberg
|
||||||
|
* `Issue 138`_: cant_write_to_target error when setup_requires is used.
|
||||||
|
* `Issue 147`_: respect the sys.dont_write_bytecode flag
|
||||||
|
|
||||||
|
------
|
||||||
|
0.6.10
|
||||||
|
------
|
||||||
|
|
||||||
|
* Reverted change made for the DistributionNotFound exception because
|
||||||
|
zc.buildout uses the exception message to get the name of the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.9
|
||||||
|
-----
|
||||||
|
|
||||||
|
* `Issue 90`_: unknown setuptools version can be added in the working set
|
||||||
|
* `Issue 87`_: setupt.py doesn't try to convert distribute_setup.py anymore
|
||||||
|
Initial Patch by arfrever.
|
||||||
|
* `Issue 89`_: added a side bar with a download link to the doc.
|
||||||
|
* `Issue 86`_: fixed missing sentence in pkg_resources doc.
|
||||||
|
* Added a nicer error message when a DistributionNotFound is raised.
|
||||||
|
* `Issue 80`_: test_develop now works with Python 3.1
|
||||||
|
* `Issue 93`_: upload_docs now works if there is an empty sub-directory.
|
||||||
|
* `Issue 70`_: exec bit on non-exec files
|
||||||
|
* `Issue 99`_: now the standalone easy_install command doesn't uses a
|
||||||
|
"setup.cfg" if any exists in the working directory. It will use it
|
||||||
|
only if triggered by ``install_requires`` from a setup.py call
|
||||||
|
(install, develop, etc).
|
||||||
|
* `Issue 101`_: Allowing ``os.devnull`` in Sandbox
|
||||||
|
* `Issue 92`_: Fixed the "no eggs" found error with MacPort
|
||||||
|
(platform.mac_ver() fails)
|
||||||
|
* `Issue 103`_: test_get_script_header_jython_workaround not run
|
||||||
|
anymore under py3 with C or POSIX local. Contributed by Arfrever.
|
||||||
|
* `Issue 104`_: remvoved the assertion when the installation fails,
|
||||||
|
with a nicer message for the end user.
|
||||||
|
* `Issue 100`_: making sure there's no SandboxViolation when
|
||||||
|
the setup script patches setuptools.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.8
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added "check_packages" in dist. (added in Setuptools 0.6c11)
|
||||||
|
* Fixed the DONT_PATCH_SETUPTOOLS state.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.7
|
||||||
|
-----
|
||||||
|
|
||||||
|
* `Issue 58`_: Added --user support to the develop command
|
||||||
|
* `Issue 11`_: Generated scripts now wrap their call to the script entry point
|
||||||
|
in the standard "if name == 'main'"
|
||||||
|
* Added the 'DONT_PATCH_SETUPTOOLS' environment variable, so virtualenv
|
||||||
|
can drive an installation that doesn't patch a global setuptools.
|
||||||
|
* Reviewed unladen-swallow specific change from
|
||||||
|
http://code.google.com/p/unladen-swallow/source/detail?spec=svn875&r=719
|
||||||
|
and determined that it no longer applies. Distribute should work fine with
|
||||||
|
Unladen Swallow 2009Q3.
|
||||||
|
* `Issue 21`_: Allow PackageIndex.open_url to gracefully handle all cases of a
|
||||||
|
httplib.HTTPException instead of just InvalidURL and BadStatusLine.
|
||||||
|
* Removed virtual-python.py from this distribution and updated documentation
|
||||||
|
to point to the actively maintained virtualenv instead.
|
||||||
|
* `Issue 64`_: use_setuptools no longer rebuilds the distribute egg every
|
||||||
|
time it is run
|
||||||
|
* use_setuptools now properly respects the requested version
|
||||||
|
* use_setuptools will no longer try to import a distribute egg for the
|
||||||
|
wrong Python version
|
||||||
|
* `Issue 74`_: no_fake should be True by default.
|
||||||
|
* `Issue 72`_: avoid a bootstrapping issue with easy_install -U
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.6
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Unified the bootstrap file so it works on both py2.x and py3k without 2to3
|
||||||
|
(patch by Holger Krekel)
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.5
|
||||||
|
-----
|
||||||
|
|
||||||
|
* `Issue 65`_: cli.exe and gui.exe are now generated at build time,
|
||||||
|
depending on the platform in use.
|
||||||
|
|
||||||
|
* `Issue 67`_: Fixed doc typo (PEP 381/382)
|
||||||
|
|
||||||
|
* Distribute no longer shadows setuptools if we require a 0.7-series
|
||||||
|
setuptools. And an error is raised when installing a 0.7 setuptools with
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
* When run from within buildout, no attempt is made to modify an existing
|
||||||
|
setuptools egg, whether in a shared egg directory or a system setuptools.
|
||||||
|
|
||||||
|
* Fixed a hole in sandboxing allowing builtin file to write outside of
|
||||||
|
the sandbox.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.4
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added the generation of `distribute_setup_3k.py` during the release.
|
||||||
|
This closes `issue #52`_.
|
||||||
|
|
||||||
|
* Added an upload_docs command to easily upload project documentation to
|
||||||
|
PyPI's http://packages.python.org. This close `issue #56`_.
|
||||||
|
|
||||||
|
* Fixed a bootstrap bug on the use_setuptools() API.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.3
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Fixed a bunch of calls to file() that caused crashes on Python 3.
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Fixed a bug in sorting that caused bootstrap to fail on Python 3.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.2
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Added Python 3 support; see docs/python3.txt.
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue39`_.
|
||||||
|
|
||||||
|
* Added option to run 2to3 automatically when installing on Python 3.
|
||||||
|
This closes `issue #31`_.
|
||||||
|
|
||||||
|
* Fixed invalid usage of requirement.parse, that broke develop -d.
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue44`_.
|
||||||
|
|
||||||
|
* Fixed script launcher for 64-bit Windows.
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue2`_.
|
||||||
|
|
||||||
|
* KeyError when compiling extensions.
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue41`_.
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Fixed bootstrap not working on Windows. This closes `issue #49`_.
|
||||||
|
|
||||||
|
* Fixed 2.6 dependencies. This closes `issue #50`_.
|
||||||
|
|
||||||
|
* Make sure setuptools is patched when running through easy_install
|
||||||
|
This closes http://bugs.python.org/setuptools/`issue40`_.
|
||||||
|
|
||||||
|
-----
|
||||||
|
0.6.1
|
||||||
|
-----
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* package_index.urlopen now catches BadStatusLine and malformed url errors.
|
||||||
|
This closes `issue #16`_ and `issue #18`_.
|
||||||
|
|
||||||
|
* zip_ok is now False by default. This closes
|
||||||
|
http://bugs.python.org/setuptools/`issue33`_.
|
||||||
|
|
||||||
|
* Fixed invalid URL error catching. http://bugs.python.org/setuptools/`issue20`_.
|
||||||
|
|
||||||
|
* Fixed invalid bootstraping with easy_install installation (`issue #40`_).
|
||||||
|
Thanks to Florian Schulze for the help.
|
||||||
|
|
||||||
|
* Removed buildout/bootstrap.py. A new repository will create a specific
|
||||||
|
bootstrap.py script.
|
||||||
|
|
||||||
|
|
||||||
|
bootstrapping
|
||||||
|
=============
|
||||||
|
|
||||||
|
* The boostrap process leave setuptools alone if detected in the system
|
||||||
|
and --root or --prefix is provided, but is not in the same location.
|
||||||
|
This closes `issue #10`_.
|
||||||
|
|
||||||
|
---
|
||||||
|
0.6
|
||||||
|
---
|
||||||
|
|
||||||
|
setuptools
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Packages required at build time where not fully present at install time.
|
||||||
|
This closes `issue #12`_.
|
||||||
|
|
||||||
|
* Protected against failures in tarfile extraction. This closes `issue #10`_.
|
||||||
|
|
||||||
|
* Made Jython api_tests.txt doctest compatible. This closes `issue #7`_.
|
||||||
|
|
||||||
|
* sandbox.py replaced builtin type file with builtin function open. This
|
||||||
|
closes `issue #6`_.
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes `issue #3`_.
|
||||||
|
|
||||||
|
* Added compatibility with Subversion 1.6. This references `issue #1`_.
|
||||||
|
|
||||||
|
pkg_resources
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Avoid a call to /usr/bin/sw_vers on OSX and use the official platform API
|
||||||
|
instead. Based on a patch from ronaldoussoren. This closes `issue #5`_.
|
||||||
|
|
||||||
|
* Fixed a SandboxViolation for mkdir that could occur in certain cases.
|
||||||
|
This closes `issue #13`_.
|
||||||
|
|
||||||
|
* Allow to find_on_path on systems with tight permissions to fail gracefully.
|
||||||
|
This closes `issue #9`_.
|
||||||
|
|
||||||
|
* Corrected inconsistency between documentation and code of add_entry.
|
||||||
|
This closes `issue #8`_.
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes `issue #3`_.
|
||||||
|
|
||||||
|
easy_install
|
||||||
|
============
|
||||||
|
|
||||||
|
* Immediately close all file handles. This closes `issue #3`_.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`Issue #135`: http://bitbucket.org/tarek/distribute/issue/135
|
||||||
|
.. _`Issue #183`: http://bitbucket.org/tarek/distribute/issue/183
|
||||||
|
.. _`Issue #207`: http://bitbucket.org/tarek/distribute/issue/207
|
||||||
|
.. _`Issue #208`: http://bitbucket.org/tarek/distribute/issue/208
|
||||||
|
.. _`Issue #212`: http://bitbucket.org/tarek/distribute/issue/212
|
||||||
|
.. _`Issue #213`: http://bitbucket.org/tarek/distribute/issue/213
|
||||||
|
.. _`Issue #218`: http://bitbucket.org/tarek/distribute/issue/218
|
||||||
|
.. _`Issue #225`: http://bitbucket.org/tarek/distribute/issue/225
|
||||||
|
.. _`Issue #227`: http://bitbucket.org/tarek/distribute/issue/227
|
||||||
|
.. _`Issue #231`: http://bitbucket.org/tarek/distribute/issue/231
|
||||||
|
.. _`Issue #237`: http://bitbucket.org/tarek/distribute/issue/237
|
||||||
|
.. _`Issue #238`: http://bitbucket.org/tarek/distribute/issue/238
|
||||||
|
.. _`Issue #239`: http://bitbucket.org/tarek/distribute/issue/239
|
||||||
|
.. _`Issue #240`: http://bitbucket.org/tarek/distribute/issue/240
|
||||||
|
.. _`Issue #241`: http://bitbucket.org/tarek/distribute/issue/241
|
||||||
|
.. _`Issue #243`: http://bitbucket.org/tarek/distribute/issue/243
|
||||||
|
.. _`Issue #244`: http://bitbucket.org/tarek/distribute/issue/244
|
||||||
|
.. _`Issue #249`: http://bitbucket.org/tarek/distribute/issue/249
|
||||||
|
.. _`Issue #258`: http://bitbucket.org/tarek/distribute/issue/258
|
||||||
|
.. _`Issue #260`: http://bitbucket.org/tarek/distribute/issue/260
|
||||||
|
.. _`Issue #262`: http://bitbucket.org/tarek/distribute/issue/262
|
||||||
|
.. _`Issue #269`: http://bitbucket.org/tarek/distribute/issue/269
|
||||||
|
.. _`Issue #272`: http://bitbucket.org/tarek/distribute/issue/272
|
||||||
|
.. _`Issue #273`: http://bitbucket.org/tarek/distribute/issue/273
|
||||||
|
.. _`Issue #283`: http://bitbucket.org/tarek/distribute/issue/283
|
||||||
|
.. _`Issue #294`: http://bitbucket.org/tarek/distribute/issue/294
|
||||||
|
.. _`Issue #299`: http://bitbucket.org/tarek/distribute/issue/299
|
||||||
|
.. _`Issue #301`: http://bitbucket.org/tarek/distribute/issue/301
|
||||||
|
.. _`Issue #303`: http://bitbucket.org/tarek/distribute/issue/303
|
||||||
|
.. _`Issue #304`: http://bitbucket.org/tarek/distribute/issue/304
|
||||||
|
.. _`Issue #305`: http://bitbucket.org/tarek/distribute/issue/305
|
||||||
|
.. _`Issue #306`: http://bitbucket.org/tarek/distribute/issue/306
|
||||||
|
.. _`Issue #307`: http://bitbucket.org/tarek/distribute/issue/307
|
||||||
|
.. _`Issue #310`: http://bitbucket.org/tarek/distribute/issue/310
|
||||||
|
.. _`Issue #311`: http://bitbucket.org/tarek/distribute/issue/311
|
||||||
|
.. _`Issue #313`: http://bitbucket.org/tarek/distribute/issue/313
|
||||||
|
.. _`Issue #314`: http://bitbucket.org/tarek/distribute/issue/314
|
||||||
|
.. _`Issue #320`: http://bitbucket.org/tarek/distribute/issue/320
|
||||||
|
.. _`Issue #323`: http://bitbucket.org/tarek/distribute/issue/323
|
||||||
|
.. _`Issue #326`: http://bitbucket.org/tarek/distribute/issue/326
|
||||||
|
.. _`Issue #327`: http://bitbucket.org/tarek/distribute/issue/327
|
||||||
|
.. _`Issue #328`: http://bitbucket.org/tarek/distribute/issue/328
|
||||||
|
.. _`Issue #329`: http://bitbucket.org/tarek/distribute/issue/329
|
||||||
|
.. _`Issue #334`: http://bitbucket.org/tarek/distribute/issue/334
|
||||||
|
.. _`Issue #335`: http://bitbucket.org/tarek/distribute/issue/335
|
||||||
|
.. _`Issue #336`: http://bitbucket.org/tarek/distribute/issue/336
|
||||||
|
.. _`Issue #341`: http://bitbucket.org/tarek/distribute/issue/341
|
||||||
|
.. _`Issue 100`: http://bitbucket.org/tarek/distribute/issue/100
|
||||||
|
.. _`Issue 101`: http://bitbucket.org/tarek/distribute/issue/101
|
||||||
|
.. _`Issue 103`: http://bitbucket.org/tarek/distribute/issue/103
|
||||||
|
.. _`Issue 104`: http://bitbucket.org/tarek/distribute/issue/104
|
||||||
|
.. _`Issue 108`: http://bitbucket.org/tarek/distribute/issue/108
|
||||||
|
.. _`Issue 11`: http://bitbucket.org/tarek/distribute/issue/11
|
||||||
|
.. _`Issue 112`: http://bitbucket.org/tarek/distribute/issue/112
|
||||||
|
.. _`Issue 121`: http://bitbucket.org/tarek/distribute/issue/121
|
||||||
|
.. _`Issue 133`: http://bitbucket.org/tarek/distribute/issue/133
|
||||||
|
.. _`Issue 134`: http://bitbucket.org/tarek/distribute/issue/134
|
||||||
|
.. _`Issue 138`: http://bitbucket.org/tarek/distribute/issue/138
|
||||||
|
.. _`Issue 143`: http://bitbucket.org/tarek/distribute/issue/143
|
||||||
|
.. _`Issue 146`: http://bitbucket.org/tarek/distribute/issue/146
|
||||||
|
.. _`Issue 147`: http://bitbucket.org/tarek/distribute/issue/147
|
||||||
|
.. _`Issue 149`: http://bitbucket.org/tarek/distribute/issue/149
|
||||||
|
.. _`Issue 15`: http://bitbucket.org/tarek/distribute/issue/15
|
||||||
|
.. _`Issue 150`: http://bitbucket.org/tarek/distribute/issue/150
|
||||||
|
.. _`Issue 160`: http://bitbucket.org/tarek/distribute/issue/160
|
||||||
|
.. _`Issue 163`: http://bitbucket.org/tarek/distribute/issue/163
|
||||||
|
.. _`Issue 170`: http://bitbucket.org/tarek/distribute/issue/170
|
||||||
|
.. _`Issue 171`: http://bitbucket.org/tarek/distribute/issue/171
|
||||||
|
.. _`Issue 174`: http://bitbucket.org/tarek/distribute/issue/174
|
||||||
|
.. _`Issue 192`: http://bitbucket.org/tarek/distribute/issue/192
|
||||||
|
.. _`Issue 193`: http://bitbucket.org/tarek/distribute/issue/193
|
||||||
|
.. _`Issue 195`: http://bitbucket.org/tarek/distribute/issue/195
|
||||||
|
.. _`Issue 196`: http://bitbucket.org/tarek/distribute/issue/196
|
||||||
|
.. _`Issue 200`: http://bitbucket.org/tarek/distribute/issue/200
|
||||||
|
.. _`Issue 204`: http://bitbucket.org/tarek/distribute/issue/204
|
||||||
|
.. _`Issue 205`: http://bitbucket.org/tarek/distribute/issue/205
|
||||||
|
.. _`Issue 206`: http://bitbucket.org/tarek/distribute/issue/206
|
||||||
|
.. _`Issue 21`: http://bitbucket.org/tarek/distribute/issue/21
|
||||||
|
.. _`Issue 210`: http://bitbucket.org/tarek/distribute/issue/210
|
||||||
|
.. _`Issue 58`: http://bitbucket.org/tarek/distribute/issue/58
|
||||||
|
.. _`Issue 64`: http://bitbucket.org/tarek/distribute/issue/64
|
||||||
|
.. _`Issue 65`: http://bitbucket.org/tarek/distribute/issue/65
|
||||||
|
.. _`Issue 67`: http://bitbucket.org/tarek/distribute/issue/67
|
||||||
|
.. _`Issue 70`: http://bitbucket.org/tarek/distribute/issue/70
|
||||||
|
.. _`Issue 72`: http://bitbucket.org/tarek/distribute/issue/72
|
||||||
|
.. _`Issue 74`: http://bitbucket.org/tarek/distribute/issue/74
|
||||||
|
.. _`Issue 80`: http://bitbucket.org/tarek/distribute/issue/80
|
||||||
|
.. _`Issue 86`: http://bitbucket.org/tarek/distribute/issue/86
|
||||||
|
.. _`Issue 87`: http://bitbucket.org/tarek/distribute/issue/87
|
||||||
|
.. _`Issue 89`: http://bitbucket.org/tarek/distribute/issue/89
|
||||||
|
.. _`Issue 90`: http://bitbucket.org/tarek/distribute/issue/90
|
||||||
|
.. _`Issue 92`: http://bitbucket.org/tarek/distribute/issue/92
|
||||||
|
.. _`Issue 93`: http://bitbucket.org/tarek/distribute/issue/93
|
||||||
|
.. _`Issue 99`: http://bitbucket.org/tarek/distribute/issue/99
|
||||||
|
.. _`issue
|
||||||
|
449`: http://bitbucket.org/tarek/distribute/issue/449
|
||||||
|
.. _`issue #1`: http://bitbucket.org/tarek/distribute/issue/1
|
||||||
|
.. _`issue #10`: http://bitbucket.org/tarek/distribute/issue/10
|
||||||
|
.. _`issue #12`: http://bitbucket.org/tarek/distribute/issue/12
|
||||||
|
.. _`issue #13`: http://bitbucket.org/tarek/distribute/issue/13
|
||||||
|
.. _`issue #16`: http://bitbucket.org/tarek/distribute/issue/16
|
||||||
|
.. _`issue #18`: http://bitbucket.org/tarek/distribute/issue/18
|
||||||
|
.. _`issue #3`: http://bitbucket.org/tarek/distribute/issue/3
|
||||||
|
.. _`issue #31`: http://bitbucket.org/tarek/distribute/issue/31
|
||||||
|
.. _`issue #40`: http://bitbucket.org/tarek/distribute/issue/40
|
||||||
|
.. _`issue #49`: http://bitbucket.org/tarek/distribute/issue/49
|
||||||
|
.. _`issue #5`: http://bitbucket.org/tarek/distribute/issue/5
|
||||||
|
.. _`issue #50`: http://bitbucket.org/tarek/distribute/issue/50
|
||||||
|
.. _`issue #52`: http://bitbucket.org/tarek/distribute/issue/52
|
||||||
|
.. _`issue #56`: http://bitbucket.org/tarek/distribute/issue/56
|
||||||
|
.. _`issue #6`: http://bitbucket.org/tarek/distribute/issue/6
|
||||||
|
.. _`issue #7`: http://bitbucket.org/tarek/distribute/issue/7
|
||||||
|
.. _`issue #8`: http://bitbucket.org/tarek/distribute/issue/8
|
||||||
|
.. _`issue #9`: http://bitbucket.org/tarek/distribute/issue/9
|
||||||
|
.. _`issue1980`: http://bitbucket.org/tarek/distribute/issue/1980
|
||||||
|
.. _`issue1981`: http://bitbucket.org/tarek/distribute/issue/1981
|
||||||
|
.. _`issue2`: http://bitbucket.org/tarek/distribute/issue/2
|
||||||
|
.. _`issue20`: http://bitbucket.org/tarek/distribute/issue/20
|
||||||
|
.. _`issue33`: http://bitbucket.org/tarek/distribute/issue/33
|
||||||
|
.. _`issue39`: http://bitbucket.org/tarek/distribute/issue/39
|
||||||
|
.. _`issue40`: http://bitbucket.org/tarek/distribute/issue/40
|
||||||
|
.. _`issue41`: http://bitbucket.org/tarek/distribute/issue/41
|
||||||
|
.. _`issue44`: http://bitbucket.org/tarek/distribute/issue/44
|
||||||
|
|
||||||
|
|
||||||
|
Keywords: CPAN PyPI distutils eggs package management
|
||||||
|
Platform: UNKNOWN
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: License :: OSI Approved :: Python Software Foundation License
|
||||||
|
Classifier: License :: OSI Approved :: Zope Public License
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Programming Language :: Python :: 2.4
|
||||||
|
Classifier: Programming Language :: Python :: 2.5
|
||||||
|
Classifier: Programming Language :: Python :: 2.6
|
||||||
|
Classifier: Programming Language :: Python :: 2.7
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3.1
|
||||||
|
Classifier: Programming Language :: Python :: 3.2
|
||||||
|
Classifier: Programming Language :: Python :: 3.3
|
||||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||||
|
Classifier: Topic :: System :: Archiving :: Packaging
|
||||||
|
Classifier: Topic :: System :: Systems Administration
|
||||||
|
Classifier: Topic :: Utilities
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
CHANGES.txt
|
||||||
|
CONTRIBUTORS.txt
|
||||||
|
DEVGUIDE.txt
|
||||||
|
MANIFEST.in
|
||||||
|
README.txt
|
||||||
|
distribute_setup.py
|
||||||
|
easy_install.py
|
||||||
|
launcher.c
|
||||||
|
pkg_resources.py
|
||||||
|
release.py
|
||||||
|
setup.cfg
|
||||||
|
setup.py
|
||||||
|
site.py
|
||||||
|
_markerlib/__init__.py
|
||||||
|
_markerlib/markers.py
|
||||||
|
distribute.egg-info/PKG-INFO
|
||||||
|
distribute.egg-info/SOURCES.txt
|
||||||
|
distribute.egg-info/dependency_links.txt
|
||||||
|
distribute.egg-info/entry_points.txt
|
||||||
|
distribute.egg-info/top_level.txt
|
||||||
|
distribute.egg-info/zip-safe
|
||||||
|
docs/Makefile
|
||||||
|
docs/conf.py
|
||||||
|
docs/easy_install.txt
|
||||||
|
docs/index.txt
|
||||||
|
docs/pkg_resources.txt
|
||||||
|
docs/python3.txt
|
||||||
|
docs/roadmap.txt
|
||||||
|
docs/setuptools.txt
|
||||||
|
docs/using.txt
|
||||||
|
docs/_templates/indexsidebar.html
|
||||||
|
docs/_theme/nature/theme.conf
|
||||||
|
docs/_theme/nature/static/nature.css_t
|
||||||
|
docs/_theme/nature/static/pygments.css
|
||||||
|
docs/build/html/_sources/easy_install.txt
|
||||||
|
docs/build/html/_sources/index.txt
|
||||||
|
docs/build/html/_sources/pkg_resources.txt
|
||||||
|
docs/build/html/_sources/python3.txt
|
||||||
|
docs/build/html/_sources/roadmap.txt
|
||||||
|
docs/build/html/_sources/setuptools.txt
|
||||||
|
docs/build/html/_sources/using.txt
|
||||||
|
docs/build/html/_static/basic.css
|
||||||
|
docs/build/html/_static/nature.css
|
||||||
|
docs/build/html/_static/pygments.css
|
||||||
|
setuptools/__init__.py
|
||||||
|
setuptools/archive_util.py
|
||||||
|
setuptools/cli-32.exe
|
||||||
|
setuptools/cli-64.exe
|
||||||
|
setuptools/cli.exe
|
||||||
|
setuptools/depends.py
|
||||||
|
setuptools/dist.py
|
||||||
|
setuptools/extension.py
|
||||||
|
setuptools/gui-32.exe
|
||||||
|
setuptools/gui-64.exe
|
||||||
|
setuptools/gui.exe
|
||||||
|
setuptools/package_index.py
|
||||||
|
setuptools/sandbox.py
|
||||||
|
setuptools/script template (dev).py
|
||||||
|
setuptools/script template.py
|
||||||
|
setuptools/command/__init__.py
|
||||||
|
setuptools/command/alias.py
|
||||||
|
setuptools/command/bdist_egg.py
|
||||||
|
setuptools/command/bdist_rpm.py
|
||||||
|
setuptools/command/bdist_wininst.py
|
||||||
|
setuptools/command/build_ext.py
|
||||||
|
setuptools/command/build_py.py
|
||||||
|
setuptools/command/develop.py
|
||||||
|
setuptools/command/easy_install.py
|
||||||
|
setuptools/command/egg_info.py
|
||||||
|
setuptools/command/install.py
|
||||||
|
setuptools/command/install_egg_info.py
|
||||||
|
setuptools/command/install_lib.py
|
||||||
|
setuptools/command/install_scripts.py
|
||||||
|
setuptools/command/register.py
|
||||||
|
setuptools/command/rotate.py
|
||||||
|
setuptools/command/saveopts.py
|
||||||
|
setuptools/command/sdist.py
|
||||||
|
setuptools/command/setopt.py
|
||||||
|
setuptools/command/test.py
|
||||||
|
setuptools/command/upload.py
|
||||||
|
setuptools/command/upload_docs.py
|
||||||
|
setuptools/tests/__init__.py
|
||||||
|
setuptools/tests/doctest.py
|
||||||
|
setuptools/tests/py26compat.py
|
||||||
|
setuptools/tests/server.py
|
||||||
|
setuptools/tests/test_bdist_egg.py
|
||||||
|
setuptools/tests/test_build_ext.py
|
||||||
|
setuptools/tests/test_develop.py
|
||||||
|
setuptools/tests/test_dist_info.py
|
||||||
|
setuptools/tests/test_easy_install.py
|
||||||
|
setuptools/tests/test_markerlib.py
|
||||||
|
setuptools/tests/test_packageindex.py
|
||||||
|
setuptools/tests/test_resources.py
|
||||||
|
setuptools/tests/test_sandbox.py
|
||||||
|
setuptools/tests/test_sdist.py
|
||||||
|
setuptools/tests/test_test.py
|
||||||
|
setuptools/tests/test_upload_docs.py
|
||||||
|
setuptools/tests/win_script_wrapper.txt
|
||||||
|
setuptools/tests/indexes/test_links_priority/external.html
|
||||||
|
setuptools/tests/indexes/test_links_priority/simple/foobar/index.html
|
||||||
|
tests/api_tests.txt
|
||||||
|
tests/install_test.py
|
||||||
|
tests/manual_test.py
|
||||||
|
tests/test_distribute_setup.py
|
||||||
|
tests/shlib_test/hello.c
|
||||||
|
tests/shlib_test/hello.pyx
|
||||||
|
tests/shlib_test/hellolib.c
|
||||||
|
tests/shlib_test/setup.py
|
||||||
|
tests/shlib_test/test_hello.py
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
[distutils.commands]
|
||||||
|
bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
|
||||||
|
rotate = setuptools.command.rotate:rotate
|
||||||
|
develop = setuptools.command.develop:develop
|
||||||
|
setopt = setuptools.command.setopt:setopt
|
||||||
|
build_py = setuptools.command.build_py:build_py
|
||||||
|
saveopts = setuptools.command.saveopts:saveopts
|
||||||
|
egg_info = setuptools.command.egg_info:egg_info
|
||||||
|
register = setuptools.command.register:register
|
||||||
|
upload_docs = setuptools.command.upload_docs:upload_docs
|
||||||
|
install_egg_info = setuptools.command.install_egg_info:install_egg_info
|
||||||
|
alias = setuptools.command.alias:alias
|
||||||
|
easy_install = setuptools.command.easy_install:easy_install
|
||||||
|
install_scripts = setuptools.command.install_scripts:install_scripts
|
||||||
|
bdist_wininst = setuptools.command.bdist_wininst:bdist_wininst
|
||||||
|
bdist_egg = setuptools.command.bdist_egg:bdist_egg
|
||||||
|
install = setuptools.command.install:install
|
||||||
|
test = setuptools.command.test:test
|
||||||
|
install_lib = setuptools.command.install_lib:install_lib
|
||||||
|
build_ext = setuptools.command.build_ext:build_ext
|
||||||
|
sdist = setuptools.command.sdist:sdist
|
||||||
|
|
||||||
|
[egg_info.writers]
|
||||||
|
dependency_links.txt = setuptools.command.egg_info:overwrite_arg
|
||||||
|
requires.txt = setuptools.command.egg_info:write_requirements
|
||||||
|
PKG-INFO = setuptools.command.egg_info:write_pkg_info
|
||||||
|
eager_resources.txt = setuptools.command.egg_info:overwrite_arg
|
||||||
|
top_level.txt = setuptools.command.egg_info:write_toplevel_names
|
||||||
|
namespace_packages.txt = setuptools.command.egg_info:overwrite_arg
|
||||||
|
entry_points.txt = setuptools.command.egg_info:write_entries
|
||||||
|
depends.txt = setuptools.command.egg_info:warn_depends_obsolete
|
||||||
|
|
||||||
|
[console_scripts]
|
||||||
|
easy_install = setuptools.command.easy_install:main
|
||||||
|
easy_install-2.7 = setuptools.command.easy_install:main
|
||||||
|
|
||||||
|
[setuptools.file_finders]
|
||||||
|
svn_cvs = setuptools.command.sdist:_default_revctrl
|
||||||
|
|
||||||
|
[distutils.setup_keywords]
|
||||||
|
dependency_links = setuptools.dist:assert_string_list
|
||||||
|
entry_points = setuptools.dist:check_entry_points
|
||||||
|
extras_require = setuptools.dist:check_extras
|
||||||
|
use_2to3_exclude_fixers = setuptools.dist:assert_string_list
|
||||||
|
package_data = setuptools.dist:check_package_data
|
||||||
|
install_requires = setuptools.dist:check_requirements
|
||||||
|
use_2to3 = setuptools.dist:assert_bool
|
||||||
|
use_2to3_fixers = setuptools.dist:assert_string_list
|
||||||
|
include_package_data = setuptools.dist:assert_bool
|
||||||
|
exclude_package_data = setuptools.dist:check_package_data
|
||||||
|
namespace_packages = setuptools.dist:check_nsp
|
||||||
|
test_suite = setuptools.dist:check_test_suite
|
||||||
|
eager_resources = setuptools.dist:assert_string_list
|
||||||
|
zip_safe = setuptools.dist:assert_bool
|
||||||
|
test_loader = setuptools.dist:check_importable
|
||||||
|
packages = setuptools.dist:check_packages
|
||||||
|
convert_2to3_doctests = setuptools.dist:assert_string_list
|
||||||
|
tests_require = setuptools.dist:check_requirements
|
||||||
|
|
||||||
|
[setuptools.installation]
|
||||||
|
eggsecutable = setuptools.command.easy_install:bootstrap
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
_markerlib
|
||||||
|
easy_install
|
||||||
|
pkg_resources
|
||||||
|
setuptools
|
||||||
|
site
|
||||||
+546
@@ -0,0 +1,546 @@
|
|||||||
|
#!python
|
||||||
|
"""Bootstrap distribute installation
|
||||||
|
|
||||||
|
If you want to use setuptools in your package's setup.py, just include this
|
||||||
|
file in the same directory with it, and add this to the top of your setup.py::
|
||||||
|
|
||||||
|
from distribute_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
|
||||||
|
If you want to require a specific version of setuptools, set a download
|
||||||
|
mirror, or use an alternate download directory, you can do so by supplying
|
||||||
|
the appropriate options to ``use_setuptools()``.
|
||||||
|
|
||||||
|
This file can also be run as a script to install or upgrade setuptools.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import fnmatch
|
||||||
|
import tempfile
|
||||||
|
import tarfile
|
||||||
|
import optparse
|
||||||
|
|
||||||
|
from distutils import log
|
||||||
|
|
||||||
|
try:
|
||||||
|
from site import USER_SITE
|
||||||
|
except ImportError:
|
||||||
|
USER_SITE = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
def _python_cmd(*args):
|
||||||
|
args = (sys.executable,) + args
|
||||||
|
return subprocess.call(args) == 0
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
# will be used for python 2.3
|
||||||
|
def _python_cmd(*args):
|
||||||
|
args = (sys.executable,) + args
|
||||||
|
# quoting arguments if windows
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
def quote(arg):
|
||||||
|
if ' ' in arg:
|
||||||
|
return '"%s"' % arg
|
||||||
|
return arg
|
||||||
|
args = [quote(arg) for arg in args]
|
||||||
|
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
|
||||||
|
|
||||||
|
DEFAULT_VERSION = "0.6.34"
|
||||||
|
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
|
||||||
|
SETUPTOOLS_FAKED_VERSION = "0.6c11"
|
||||||
|
|
||||||
|
SETUPTOOLS_PKG_INFO = """\
|
||||||
|
Metadata-Version: 1.0
|
||||||
|
Name: setuptools
|
||||||
|
Version: %s
|
||||||
|
Summary: xxxx
|
||||||
|
Home-page: xxx
|
||||||
|
Author: xxx
|
||||||
|
Author-email: xxx
|
||||||
|
License: xxx
|
||||||
|
Description: xxx
|
||||||
|
""" % SETUPTOOLS_FAKED_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
def _install(tarball, install_args=()):
|
||||||
|
# extracting the tarball
|
||||||
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
log.warn('Extracting in %s', tmpdir)
|
||||||
|
old_wd = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir(tmpdir)
|
||||||
|
tar = tarfile.open(tarball)
|
||||||
|
_extractall(tar)
|
||||||
|
tar.close()
|
||||||
|
|
||||||
|
# going in the directory
|
||||||
|
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||||
|
os.chdir(subdir)
|
||||||
|
log.warn('Now working in %s', subdir)
|
||||||
|
|
||||||
|
# installing
|
||||||
|
log.warn('Installing Distribute')
|
||||||
|
if not _python_cmd('setup.py', 'install', *install_args):
|
||||||
|
log.warn('Something went wrong during the installation.')
|
||||||
|
log.warn('See the error message above.')
|
||||||
|
# exitcode will be 2
|
||||||
|
return 2
|
||||||
|
finally:
|
||||||
|
os.chdir(old_wd)
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_egg(egg, tarball, to_dir):
|
||||||
|
# extracting the tarball
|
||||||
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
log.warn('Extracting in %s', tmpdir)
|
||||||
|
old_wd = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir(tmpdir)
|
||||||
|
tar = tarfile.open(tarball)
|
||||||
|
_extractall(tar)
|
||||||
|
tar.close()
|
||||||
|
|
||||||
|
# going in the directory
|
||||||
|
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||||
|
os.chdir(subdir)
|
||||||
|
log.warn('Now working in %s', subdir)
|
||||||
|
|
||||||
|
# building an egg
|
||||||
|
log.warn('Building a Distribute egg in %s', to_dir)
|
||||||
|
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
os.chdir(old_wd)
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
# returning the result
|
||||||
|
log.warn(egg)
|
||||||
|
if not os.path.exists(egg):
|
||||||
|
raise IOError('Could not build the egg.')
|
||||||
|
|
||||||
|
|
||||||
|
def _do_download(version, download_base, to_dir, download_delay):
|
||||||
|
egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
|
||||||
|
% (version, sys.version_info[0], sys.version_info[1]))
|
||||||
|
if not os.path.exists(egg):
|
||||||
|
tarball = download_setuptools(version, download_base,
|
||||||
|
to_dir, download_delay)
|
||||||
|
_build_egg(egg, tarball, to_dir)
|
||||||
|
sys.path.insert(0, egg)
|
||||||
|
import setuptools
|
||||||
|
setuptools.bootstrap_install_from = egg
|
||||||
|
|
||||||
|
|
||||||
|
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||||
|
to_dir=os.curdir, download_delay=15, no_fake=True):
|
||||||
|
# making sure we use the absolute path
|
||||||
|
to_dir = os.path.abspath(to_dir)
|
||||||
|
was_imported = 'pkg_resources' in sys.modules or \
|
||||||
|
'setuptools' in sys.modules
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
if not hasattr(pkg_resources, '_distribute'):
|
||||||
|
if not no_fake:
|
||||||
|
_fake_setuptools()
|
||||||
|
raise ImportError
|
||||||
|
except ImportError:
|
||||||
|
return _do_download(version, download_base, to_dir, download_delay)
|
||||||
|
try:
|
||||||
|
pkg_resources.require("distribute>=" + version)
|
||||||
|
return
|
||||||
|
except pkg_resources.VersionConflict:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
if was_imported:
|
||||||
|
sys.stderr.write(
|
||||||
|
"The required version of distribute (>=%s) is not available,\n"
|
||||||
|
"and can't be installed while this script is running. Please\n"
|
||||||
|
"install a more recent version first, using\n"
|
||||||
|
"'easy_install -U distribute'."
|
||||||
|
"\n\n(Currently using %r)\n" % (version, e.args[0]))
|
||||||
|
sys.exit(2)
|
||||||
|
else:
|
||||||
|
del pkg_resources, sys.modules['pkg_resources'] # reload ok
|
||||||
|
return _do_download(version, download_base, to_dir,
|
||||||
|
download_delay)
|
||||||
|
except pkg_resources.DistributionNotFound:
|
||||||
|
return _do_download(version, download_base, to_dir,
|
||||||
|
download_delay)
|
||||||
|
finally:
|
||||||
|
if not no_fake:
|
||||||
|
_create_fake_setuptools_pkg_info(to_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||||
|
to_dir=os.curdir, delay=15):
|
||||||
|
"""Download distribute from a specified location and return its filename
|
||||||
|
|
||||||
|
`version` should be a valid distribute version number that is available
|
||||||
|
as an egg for download under the `download_base` URL (which should end
|
||||||
|
with a '/'). `to_dir` is the directory where the egg will be downloaded.
|
||||||
|
`delay` is the number of seconds to pause before an actual download
|
||||||
|
attempt.
|
||||||
|
"""
|
||||||
|
# making sure we use the absolute path
|
||||||
|
to_dir = os.path.abspath(to_dir)
|
||||||
|
try:
|
||||||
|
from urllib.request import urlopen
|
||||||
|
except ImportError:
|
||||||
|
from urllib2 import urlopen
|
||||||
|
tgz_name = "distribute-%s.tar.gz" % version
|
||||||
|
url = download_base + tgz_name
|
||||||
|
saveto = os.path.join(to_dir, tgz_name)
|
||||||
|
src = dst = None
|
||||||
|
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||||
|
try:
|
||||||
|
log.warn("Downloading %s", url)
|
||||||
|
src = urlopen(url)
|
||||||
|
# Read/write all in one block, so we don't create a corrupt file
|
||||||
|
# if the download is interrupted.
|
||||||
|
data = src.read()
|
||||||
|
dst = open(saveto, "wb")
|
||||||
|
dst.write(data)
|
||||||
|
finally:
|
||||||
|
if src:
|
||||||
|
src.close()
|
||||||
|
if dst:
|
||||||
|
dst.close()
|
||||||
|
return os.path.realpath(saveto)
|
||||||
|
|
||||||
|
|
||||||
|
def _no_sandbox(function):
|
||||||
|
def __no_sandbox(*args, **kw):
|
||||||
|
try:
|
||||||
|
from setuptools.sandbox import DirectorySandbox
|
||||||
|
if not hasattr(DirectorySandbox, '_old'):
|
||||||
|
def violation(*args):
|
||||||
|
pass
|
||||||
|
DirectorySandbox._old = DirectorySandbox._violation
|
||||||
|
DirectorySandbox._violation = violation
|
||||||
|
patched = True
|
||||||
|
else:
|
||||||
|
patched = False
|
||||||
|
except ImportError:
|
||||||
|
patched = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
return function(*args, **kw)
|
||||||
|
finally:
|
||||||
|
if patched:
|
||||||
|
DirectorySandbox._violation = DirectorySandbox._old
|
||||||
|
del DirectorySandbox._old
|
||||||
|
|
||||||
|
return __no_sandbox
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_file(path, content):
|
||||||
|
"""Will backup the file then patch it"""
|
||||||
|
f = open(path)
|
||||||
|
existing_content = f.read()
|
||||||
|
f.close()
|
||||||
|
if existing_content == content:
|
||||||
|
# already patched
|
||||||
|
log.warn('Already patched.')
|
||||||
|
return False
|
||||||
|
log.warn('Patching...')
|
||||||
|
_rename_path(path)
|
||||||
|
f = open(path, 'w')
|
||||||
|
try:
|
||||||
|
f.write(content)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
_patch_file = _no_sandbox(_patch_file)
|
||||||
|
|
||||||
|
|
||||||
|
def _same_content(path, content):
|
||||||
|
f = open(path)
|
||||||
|
existing_content = f.read()
|
||||||
|
f.close()
|
||||||
|
return existing_content == content
|
||||||
|
|
||||||
|
|
||||||
|
def _rename_path(path):
|
||||||
|
new_name = path + '.OLD.%s' % time.time()
|
||||||
|
log.warn('Renaming %s to %s', path, new_name)
|
||||||
|
os.rename(path, new_name)
|
||||||
|
return new_name
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_flat_installation(placeholder):
|
||||||
|
if not os.path.isdir(placeholder):
|
||||||
|
log.warn('Unkown installation at %s', placeholder)
|
||||||
|
return False
|
||||||
|
found = False
|
||||||
|
for file in os.listdir(placeholder):
|
||||||
|
if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
log.warn('Could not locate setuptools*.egg-info')
|
||||||
|
return
|
||||||
|
|
||||||
|
log.warn('Moving elements out of the way...')
|
||||||
|
pkg_info = os.path.join(placeholder, file)
|
||||||
|
if os.path.isdir(pkg_info):
|
||||||
|
patched = _patch_egg_dir(pkg_info)
|
||||||
|
else:
|
||||||
|
patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
|
||||||
|
|
||||||
|
if not patched:
|
||||||
|
log.warn('%s already patched.', pkg_info)
|
||||||
|
return False
|
||||||
|
# now let's move the files out of the way
|
||||||
|
for element in ('setuptools', 'pkg_resources.py', 'site.py'):
|
||||||
|
element = os.path.join(placeholder, element)
|
||||||
|
if os.path.exists(element):
|
||||||
|
_rename_path(element)
|
||||||
|
else:
|
||||||
|
log.warn('Could not find the %s element of the '
|
||||||
|
'Setuptools distribution', element)
|
||||||
|
return True
|
||||||
|
|
||||||
|
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
|
||||||
|
|
||||||
|
|
||||||
|
def _after_install(dist):
|
||||||
|
log.warn('After install bootstrap.')
|
||||||
|
placeholder = dist.get_command_obj('install').install_purelib
|
||||||
|
_create_fake_setuptools_pkg_info(placeholder)
|
||||||
|
|
||||||
|
|
||||||
|
def _create_fake_setuptools_pkg_info(placeholder):
|
||||||
|
if not placeholder or not os.path.exists(placeholder):
|
||||||
|
log.warn('Could not find the install location')
|
||||||
|
return
|
||||||
|
pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
|
||||||
|
setuptools_file = 'setuptools-%s-py%s.egg-info' % \
|
||||||
|
(SETUPTOOLS_FAKED_VERSION, pyver)
|
||||||
|
pkg_info = os.path.join(placeholder, setuptools_file)
|
||||||
|
if os.path.exists(pkg_info):
|
||||||
|
log.warn('%s already exists', pkg_info)
|
||||||
|
return
|
||||||
|
|
||||||
|
log.warn('Creating %s', pkg_info)
|
||||||
|
try:
|
||||||
|
f = open(pkg_info, 'w')
|
||||||
|
except EnvironmentError:
|
||||||
|
log.warn("Don't have permissions to write %s, skipping", pkg_info)
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
f.write(SETUPTOOLS_PKG_INFO)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
pth_file = os.path.join(placeholder, 'setuptools.pth')
|
||||||
|
log.warn('Creating %s', pth_file)
|
||||||
|
f = open(pth_file, 'w')
|
||||||
|
try:
|
||||||
|
f.write(os.path.join(os.curdir, setuptools_file))
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
_create_fake_setuptools_pkg_info = _no_sandbox(
|
||||||
|
_create_fake_setuptools_pkg_info
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_egg_dir(path):
|
||||||
|
# let's check if it's already patched
|
||||||
|
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
|
||||||
|
if os.path.exists(pkg_info):
|
||||||
|
if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
|
||||||
|
log.warn('%s already patched.', pkg_info)
|
||||||
|
return False
|
||||||
|
_rename_path(path)
|
||||||
|
os.mkdir(path)
|
||||||
|
os.mkdir(os.path.join(path, 'EGG-INFO'))
|
||||||
|
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
|
||||||
|
f = open(pkg_info, 'w')
|
||||||
|
try:
|
||||||
|
f.write(SETUPTOOLS_PKG_INFO)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def _before_install():
|
||||||
|
log.warn('Before install bootstrap.')
|
||||||
|
_fake_setuptools()
|
||||||
|
|
||||||
|
|
||||||
|
def _under_prefix(location):
|
||||||
|
if 'install' not in sys.argv:
|
||||||
|
return True
|
||||||
|
args = sys.argv[sys.argv.index('install') + 1:]
|
||||||
|
for index, arg in enumerate(args):
|
||||||
|
for option in ('--root', '--prefix'):
|
||||||
|
if arg.startswith('%s=' % option):
|
||||||
|
top_dir = arg.split('root=')[-1]
|
||||||
|
return location.startswith(top_dir)
|
||||||
|
elif arg == option:
|
||||||
|
if len(args) > index:
|
||||||
|
top_dir = args[index + 1]
|
||||||
|
return location.startswith(top_dir)
|
||||||
|
if arg == '--user' and USER_SITE is not None:
|
||||||
|
return location.startswith(USER_SITE)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _fake_setuptools():
|
||||||
|
log.warn('Scanning installed packages')
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
except ImportError:
|
||||||
|
# we're cool
|
||||||
|
log.warn('Setuptools or Distribute does not seem to be installed.')
|
||||||
|
return
|
||||||
|
ws = pkg_resources.working_set
|
||||||
|
try:
|
||||||
|
setuptools_dist = ws.find(
|
||||||
|
pkg_resources.Requirement.parse('setuptools', replacement=False)
|
||||||
|
)
|
||||||
|
except TypeError:
|
||||||
|
# old distribute API
|
||||||
|
setuptools_dist = ws.find(
|
||||||
|
pkg_resources.Requirement.parse('setuptools')
|
||||||
|
)
|
||||||
|
|
||||||
|
if setuptools_dist is None:
|
||||||
|
log.warn('No setuptools distribution found')
|
||||||
|
return
|
||||||
|
# detecting if it was already faked
|
||||||
|
setuptools_location = setuptools_dist.location
|
||||||
|
log.warn('Setuptools installation detected at %s', setuptools_location)
|
||||||
|
|
||||||
|
# if --root or --preix was provided, and if
|
||||||
|
# setuptools is not located in them, we don't patch it
|
||||||
|
if not _under_prefix(setuptools_location):
|
||||||
|
log.warn('Not patching, --root or --prefix is installing Distribute'
|
||||||
|
' in another location')
|
||||||
|
return
|
||||||
|
|
||||||
|
# let's see if its an egg
|
||||||
|
if not setuptools_location.endswith('.egg'):
|
||||||
|
log.warn('Non-egg installation')
|
||||||
|
res = _remove_flat_installation(setuptools_location)
|
||||||
|
if not res:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
log.warn('Egg installation')
|
||||||
|
pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
|
||||||
|
if (os.path.exists(pkg_info) and
|
||||||
|
_same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
|
||||||
|
log.warn('Already patched.')
|
||||||
|
return
|
||||||
|
log.warn('Patching...')
|
||||||
|
# let's create a fake egg replacing setuptools one
|
||||||
|
res = _patch_egg_dir(setuptools_location)
|
||||||
|
if not res:
|
||||||
|
return
|
||||||
|
log.warn('Patching complete.')
|
||||||
|
_relaunch()
|
||||||
|
|
||||||
|
|
||||||
|
def _relaunch():
|
||||||
|
log.warn('Relaunching...')
|
||||||
|
# we have to relaunch the process
|
||||||
|
# pip marker to avoid a relaunch bug
|
||||||
|
_cmd1 = ['-c', 'install', '--single-version-externally-managed']
|
||||||
|
_cmd2 = ['-c', 'install', '--record']
|
||||||
|
if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
|
||||||
|
sys.argv[0] = 'setup.py'
|
||||||
|
args = [sys.executable] + sys.argv
|
||||||
|
sys.exit(subprocess.call(args))
|
||||||
|
|
||||||
|
|
||||||
|
def _extractall(self, path=".", members=None):
|
||||||
|
"""Extract all members from the archive to the current working
|
||||||
|
directory and set owner, modification time and permissions on
|
||||||
|
directories afterwards. `path' specifies a different directory
|
||||||
|
to extract to. `members' is optional and must be a subset of the
|
||||||
|
list returned by getmembers().
|
||||||
|
"""
|
||||||
|
import copy
|
||||||
|
import operator
|
||||||
|
from tarfile import ExtractError
|
||||||
|
directories = []
|
||||||
|
|
||||||
|
if members is None:
|
||||||
|
members = self
|
||||||
|
|
||||||
|
for tarinfo in members:
|
||||||
|
if tarinfo.isdir():
|
||||||
|
# Extract directories with a safe mode.
|
||||||
|
directories.append(tarinfo)
|
||||||
|
tarinfo = copy.copy(tarinfo)
|
||||||
|
tarinfo.mode = 448 # decimal for oct 0700
|
||||||
|
self.extract(tarinfo, path)
|
||||||
|
|
||||||
|
# Reverse sort directories.
|
||||||
|
if sys.version_info < (2, 4):
|
||||||
|
def sorter(dir1, dir2):
|
||||||
|
return cmp(dir1.name, dir2.name)
|
||||||
|
directories.sort(sorter)
|
||||||
|
directories.reverse()
|
||||||
|
else:
|
||||||
|
directories.sort(key=operator.attrgetter('name'), reverse=True)
|
||||||
|
|
||||||
|
# Set correct owner, mtime and filemode on directories.
|
||||||
|
for tarinfo in directories:
|
||||||
|
dirpath = os.path.join(path, tarinfo.name)
|
||||||
|
try:
|
||||||
|
self.chown(tarinfo, dirpath)
|
||||||
|
self.utime(tarinfo, dirpath)
|
||||||
|
self.chmod(tarinfo, dirpath)
|
||||||
|
except ExtractError:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
if self.errorlevel > 1:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
self._dbg(1, "tarfile: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_install_args(options):
|
||||||
|
"""
|
||||||
|
Build the arguments to 'python setup.py install' on the distribute package
|
||||||
|
"""
|
||||||
|
install_args = []
|
||||||
|
if options.user_install:
|
||||||
|
if sys.version_info < (2, 6):
|
||||||
|
log.warn("--user requires Python 2.6 or later")
|
||||||
|
raise SystemExit(1)
|
||||||
|
install_args.append('--user')
|
||||||
|
return install_args
|
||||||
|
|
||||||
|
def _parse_args():
|
||||||
|
"""
|
||||||
|
Parse the command line for options
|
||||||
|
"""
|
||||||
|
parser = optparse.OptionParser()
|
||||||
|
parser.add_option(
|
||||||
|
'--user', dest='user_install', action='store_true', default=False,
|
||||||
|
help='install in user site package (requires Python 2.6 or later)')
|
||||||
|
parser.add_option(
|
||||||
|
'--download-base', dest='download_base', metavar="URL",
|
||||||
|
default=DEFAULT_URL,
|
||||||
|
help='alternative URL from where to download the distribute package')
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
# positional arguments are ignored
|
||||||
|
return options
|
||||||
|
|
||||||
|
def main(version=DEFAULT_VERSION):
|
||||||
|
"""Install or upgrade setuptools and EasyInstall"""
|
||||||
|
options = _parse_args()
|
||||||
|
tarball = download_setuptools(download_base=options.download_base)
|
||||||
|
return _install(tarball, _build_install_args(options))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
# Makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
PAPER =
|
||||||
|
|
||||||
|
# Internal variables.
|
||||||
|
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||||
|
PAPEROPT_letter = -D latex_paper_size=letter
|
||||||
|
ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||||
|
|
||||||
|
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Please use \`make <target>' where <target> is one of"
|
||||||
|
@echo " html to make standalone HTML files"
|
||||||
|
@echo " pickle to make pickle files"
|
||||||
|
@echo " json to make JSON files"
|
||||||
|
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||||
|
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||||
|
@echo " changes to make an overview over all changed/added/deprecated items"
|
||||||
|
@echo " linkcheck to check all external links for integrity"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -rf build/*
|
||||||
|
|
||||||
|
html:
|
||||||
|
mkdir -p build/html build/doctrees
|
||||||
|
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in build/html."
|
||||||
|
|
||||||
|
pickle:
|
||||||
|
mkdir -p build/pickle build/doctrees
|
||||||
|
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the pickle files."
|
||||||
|
|
||||||
|
web: pickle
|
||||||
|
|
||||||
|
json:
|
||||||
|
mkdir -p build/json build/doctrees
|
||||||
|
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the JSON files."
|
||||||
|
|
||||||
|
htmlhelp:
|
||||||
|
mkdir -p build/htmlhelp build/doctrees
|
||||||
|
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||||
|
".hhp project file in build/htmlhelp."
|
||||||
|
|
||||||
|
latex:
|
||||||
|
mkdir -p build/latex build/doctrees
|
||||||
|
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; the LaTeX files are in build/latex."
|
||||||
|
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||||
|
"run these through (pdf)latex."
|
||||||
|
|
||||||
|
changes:
|
||||||
|
mkdir -p build/changes build/doctrees
|
||||||
|
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
|
||||||
|
@echo
|
||||||
|
@echo "The overview file is in build/changes."
|
||||||
|
|
||||||
|
linkcheck:
|
||||||
|
mkdir -p build/linkcheck build/doctrees
|
||||||
|
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
|
||||||
|
@echo
|
||||||
|
@echo "Link check complete; look for any errors in the above output " \
|
||||||
|
"or in build/linkcheck/output.txt."
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<h3>Download</h3>
|
||||||
|
|
||||||
|
<p>Current version: <b>{{ version }}</b></p>
|
||||||
|
<p>Get Distribute from the <a href="http://pypi.python.org/pypi/distribute"> Python Package Index</a>
|
||||||
|
|
||||||
|
<h3>Questions? Suggestions? Contributions?</h3>
|
||||||
|
|
||||||
|
<p>Visit the <a href="http://bitbucket.org/tarek/distribute">Distribute project page</a> </p>
|
||||||
+62
-54
@@ -10,8 +10,8 @@
|
|||||||
body {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
background-color: #111;
|
background-color: #111111;
|
||||||
color: #555;
|
color: #555555;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ div.documentwrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.bodywrapper {
|
div.bodywrapper {
|
||||||
margin: 0 0 0 230px;
|
margin: 0 0 0 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr{
|
hr{
|
||||||
@@ -30,14 +30,14 @@ hr{
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.document {
|
div.document {
|
||||||
background-color: #eee;
|
background-color: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.body {
|
div.body {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
color: #3E4349;
|
color: #3E4349;
|
||||||
padding: 0 30px 30px 30px;
|
padding: 1em 30px 30px 30px;
|
||||||
font-size: 0.8em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.footer {
|
div.footer {
|
||||||
@@ -49,25 +49,29 @@ div.footer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.footer a {
|
div.footer a {
|
||||||
color: #444;
|
color: #444444;
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.related {
|
div.related {
|
||||||
background-color: #6BA81E;
|
background-color: #6BA81E;
|
||||||
line-height: 32px;
|
line-height: 36px;
|
||||||
color: #fff;
|
color: #ffffff;
|
||||||
text-shadow: 0px 1px 0 #444;
|
text-shadow: 0px 1px 0 #444444;
|
||||||
font-size: 0.80em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.related a {
|
div.related a {
|
||||||
color: #E2F3CC;
|
color: #E2F3CC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.related .right {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
div.sphinxsidebar {
|
div.sphinxsidebar {
|
||||||
font-size: 0.75em;
|
font-size: 0.9em;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
|
width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebarwrapper{
|
div.sphinxsidebarwrapper{
|
||||||
@@ -77,46 +81,46 @@ div.sphinxsidebarwrapper{
|
|||||||
div.sphinxsidebar h3,
|
div.sphinxsidebar h3,
|
||||||
div.sphinxsidebar h4 {
|
div.sphinxsidebar h4 {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
color: #222;
|
color: #222222;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
font-weight: normal;
|
font-weight: bold;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
background-color: #ddd;
|
|
||||||
text-shadow: 1px 1px 0 white
|
text-shadow: 1px 1px 0 white
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar h4{
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h3 a {
|
div.sphinxsidebar h3 a {
|
||||||
color: #444;
|
color: #444444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
div.sphinxsidebar p {
|
div.sphinxsidebar p {
|
||||||
color: #888;
|
color: #888888;
|
||||||
padding: 5px 20px;
|
padding: 5px 20px;
|
||||||
|
margin: 0.5em 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar p.topless {
|
div.sphinxsidebar p.topless {
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar ul {
|
div.sphinxsidebar ul {
|
||||||
margin: 10px 20px;
|
margin: 10px 10px 10px 20px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: #000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar a {
|
div.sphinxsidebar a {
|
||||||
color: #444;
|
color: #444444;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar a:hover {
|
||||||
|
color: #E32E00;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar input {
|
div.sphinxsidebar input {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #cccccc;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-size: 1em;
|
font-size: 1.1em;
|
||||||
|
padding: 0.15em 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar input[type=text]{
|
div.sphinxsidebar input[type=text]{
|
||||||
@@ -132,7 +136,6 @@ a {
|
|||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #E32E00;
|
color: #E32E00;
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.body h1,
|
div.body h1,
|
||||||
@@ -142,20 +145,20 @@ div.body h4,
|
|||||||
div.body h5,
|
div.body h5,
|
||||||
div.body h6 {
|
div.body h6 {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
background-color: #BED4EB;
|
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
color: #212224;
|
color: #212224;
|
||||||
margin: 30px 0px 10px 0px;
|
margin: 30px 0px 10px 0px;
|
||||||
padding: 5px 0 5px 10px;
|
padding: 5px 0 5px 0px;
|
||||||
text-shadow: 0px 1px 0 white
|
text-shadow: 0px 1px 0 white;
|
||||||
|
border-bottom: 1px solid #C8D5E3;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
|
div.body h1 { margin-top: 0; font-size: 200%; }
|
||||||
div.body h2 { font-size: 150%; background-color: #C8D5E3; }
|
div.body h2 { font-size: 150%; }
|
||||||
div.body h3 { font-size: 120%; background-color: #D8DEE3; }
|
div.body h3 { font-size: 120%; }
|
||||||
div.body h4 { font-size: 110%; background-color: #D8DEE3; }
|
div.body h4 { font-size: 110%; }
|
||||||
div.body h5 { font-size: 100%; background-color: #D8DEE3; }
|
div.body h5 { font-size: 100%; }
|
||||||
div.body h6 { font-size: 100%; background-color: #D8DEE3; }
|
div.body h6 { font-size: 100%; }
|
||||||
|
|
||||||
a.headerlink {
|
a.headerlink {
|
||||||
color: #c60f0f;
|
color: #c60f0f;
|
||||||
@@ -170,7 +173,7 @@ a.headerlink:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.body p, div.body dd, div.body li {
|
div.body p, div.body dd, div.body li {
|
||||||
line-height: 1.5em;
|
line-height: 1.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.admonition p.admonition-title + p {
|
div.admonition p.admonition-title + p {
|
||||||
@@ -182,22 +185,23 @@ div.highlight{
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.note {
|
div.note {
|
||||||
background-color: #eee;
|
background-color: #eeeeee;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #cccccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.seealso {
|
div.seealso {
|
||||||
background-color: #ffc;
|
background-color: #ffffcc;
|
||||||
border: 1px solid #ff6;
|
border: 1px solid #ffff66;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.topic {
|
div.topic {
|
||||||
background-color: #eee;
|
background-color: #fafafa;
|
||||||
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.warning {
|
div.warning {
|
||||||
background-color: #ffe4e4;
|
background-color: #ffe4e4;
|
||||||
border: 1px solid #f66;
|
border: 1px solid #ff6666;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.admonition-title {
|
p.admonition-title {
|
||||||
@@ -210,20 +214,24 @@ p.admonition-title:after {
|
|||||||
|
|
||||||
pre {
|
pre {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: White;
|
background-color: #fafafa;
|
||||||
color: #222;
|
color: #222222;
|
||||||
line-height: 1.2em;
|
line-height: 1.5em;
|
||||||
border: 1px solid #C6C9CB;
|
font-size: 1.1em;
|
||||||
font-size: 1.2em;
|
|
||||||
margin: 1.5em 0 1.5em 0;
|
margin: 1.5em 0 1.5em 0;
|
||||||
-webkit-box-shadow: 1px 1px 1px #d8d8d8;
|
-webkit-box-shadow: 0px 0px 4px #d8d8d8;
|
||||||
-moz-box-shadow: 1px 1px 1px #d8d8d8;
|
-moz-box-shadow: 0px 0px 4px #d8d8d8;
|
||||||
|
box-shadow: 0px 0px 4px #d8d8d8;
|
||||||
}
|
}
|
||||||
|
|
||||||
tt {
|
tt {
|
||||||
background-color: #ecf0f3;
|
color: #222222;
|
||||||
color: #222;
|
|
||||||
padding: 1px 2px;
|
padding: 1px 2px;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#table-of-contents ul {
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,36 @@
|
|||||||
|
Welcome to Distribute's documentation!
|
||||||
|
======================================
|
||||||
|
|
||||||
|
`Distribute` is a fork of the `Setuptools` project.
|
||||||
|
|
||||||
|
Distribute is intended to replace Setuptools as the standard method for
|
||||||
|
working with Python module distributions.
|
||||||
|
|
||||||
|
For those who may wonder why they should switch to Distribute over Setuptools, it’s quite simple:
|
||||||
|
|
||||||
|
- Distribute is a drop-in replacement for Setuptools
|
||||||
|
- The code is actively maintained, and has over 10 commiters
|
||||||
|
- Distribute offers Python 3 support !
|
||||||
|
|
||||||
|
Documentation content:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
roadmap
|
||||||
|
python3
|
||||||
|
using
|
||||||
|
setuptools
|
||||||
|
easy_install
|
||||||
|
pkg_resources
|
||||||
|
|
||||||
|
|
||||||
|
.. image:: http://python-distribute.org/pip_distribute.png
|
||||||
|
|
||||||
|
Design done by Idan Gazit (http://pixane.com) - License: cc-by-3.0
|
||||||
|
|
||||||
|
Copy & paste::
|
||||||
|
|
||||||
|
curl -O http://python-distribute.org/distribute_setup.py
|
||||||
|
python distribute_setup.py
|
||||||
|
easy_install pip
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,121 @@
|
|||||||
|
=====================================================
|
||||||
|
Supporting both Python 2 and Python 3 with Distribute
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
Starting with version 0.6.2, Distribute supports Python 3. Installing and
|
||||||
|
using distribute for Python 3 code works exactly the same as for Python 2
|
||||||
|
code, but Distribute also helps you to support Python 2 and Python 3 from
|
||||||
|
the same source code by letting you run 2to3 on the code as a part of the
|
||||||
|
build process, by setting the keyword parameter ``use_2to3`` to True.
|
||||||
|
|
||||||
|
|
||||||
|
Distribute as help during porting
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Distribute can make the porting process much easier by automatically running
|
||||||
|
2to3 as a part of the test running. To do this you need to configure the
|
||||||
|
setup.py so that you can run the unit tests with ``python setup.py test``.
|
||||||
|
|
||||||
|
See :ref:`test` for more information on this.
|
||||||
|
|
||||||
|
Once you have the tests running under Python 2, you can add the use_2to3
|
||||||
|
keyword parameters to setup(), and start running the tests under Python 3.
|
||||||
|
The test command will now first run the build command during which the code
|
||||||
|
will be converted with 2to3, and the tests will then be run from the build
|
||||||
|
directory, as opposed from the source directory as is normally done.
|
||||||
|
|
||||||
|
Distribute will convert all Python files, and also all doctests in Python
|
||||||
|
files. However, if you have doctests located in separate text files, these
|
||||||
|
will not automatically be converted. By adding them to the
|
||||||
|
``convert_2to3_doctests`` keyword parameter Distrubute will convert them as
|
||||||
|
well.
|
||||||
|
|
||||||
|
By default, the conversion uses all fixers in the ``lib2to3.fixers`` package.
|
||||||
|
To use additional fixers, the parameter ``use_2to3_fixers`` can be set
|
||||||
|
to a list of names of packages containing fixers. To exclude fixers, the
|
||||||
|
parameter ``use_2to3_exclude_fixers`` can be set to fixer names to be
|
||||||
|
skipped.
|
||||||
|
|
||||||
|
A typical setup.py can look something like this::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='your.module',
|
||||||
|
version = '1.0',
|
||||||
|
description='This is your awesome module',
|
||||||
|
author='You',
|
||||||
|
author_email='your@email',
|
||||||
|
package_dir = {'': 'src'},
|
||||||
|
packages = ['your', 'you.module'],
|
||||||
|
test_suite = 'your.module.tests',
|
||||||
|
use_2to3 = True,
|
||||||
|
convert_2to3_doctests = ['src/your/module/README.txt'],
|
||||||
|
use_2to3_fixers = ['your.fixers'],
|
||||||
|
use_2to3_exclude_fixers = ['lib2to3.fixes.fix_import'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Differential conversion
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Note that a file will only be copied and converted during the build process
|
||||||
|
if the source file has been changed. If you add a file to the doctests
|
||||||
|
that should be converted, it will not be converted the next time you run
|
||||||
|
the tests, since it hasn't been modified. You need to remove it from the
|
||||||
|
build directory. Also if you run the build, install or test commands before
|
||||||
|
adding the use_2to3 parameter, you will have to remove the build directory
|
||||||
|
before you run the test command, as the files otherwise will seem updated,
|
||||||
|
and no conversion will happen.
|
||||||
|
|
||||||
|
In general, if code doesn't seem to be converted, deleting the build directory
|
||||||
|
and trying again is a good saferguard against the build directory getting
|
||||||
|
"out of sync" with the source directory.
|
||||||
|
|
||||||
|
Distributing Python 3 modules
|
||||||
|
=============================
|
||||||
|
|
||||||
|
You can distribute your modules with Python 3 support in different ways. A
|
||||||
|
normal source distribution will work, but can be slow in installing, as the
|
||||||
|
2to3 process will be run during the install. But you can also distribute
|
||||||
|
the module in binary format, such as a binary egg. That egg will contain the
|
||||||
|
already converted code, and hence no 2to3 conversion is needed during install.
|
||||||
|
|
||||||
|
Advanced features
|
||||||
|
=================
|
||||||
|
|
||||||
|
If you don't want to run the 2to3 conversion on the doctests in Python files,
|
||||||
|
you can turn that off by setting ``setuptools.use_2to3_on_doctests = False``.
|
||||||
|
|
||||||
|
Note on compatibility with setuptools
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Setuptools do not know about the new keyword parameters to support Python 3.
|
||||||
|
As a result it will warn about the unknown keyword parameters if you use
|
||||||
|
setuptools instead of Distribute under Python 2. This is not an error, and
|
||||||
|
install process will continue as normal, but if you want to get rid of that
|
||||||
|
error this is easy. Simply conditionally add the new parameters into an extra
|
||||||
|
dict and pass that dict into setup()::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
import sys
|
||||||
|
|
||||||
|
extra = {}
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
extra['use_2to3'] = True
|
||||||
|
extra['convert_2to3_doctests'] = ['src/your/module/README.txt']
|
||||||
|
extra['use_2to3_fixers'] = ['your.fixers']
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='your.module',
|
||||||
|
version = '1.0',
|
||||||
|
description='This is your awesome module',
|
||||||
|
author='You',
|
||||||
|
author_email='your@email',
|
||||||
|
package_dir = {'': 'src'},
|
||||||
|
packages = ['your', 'you.module'],
|
||||||
|
test_suite = 'your.module.tests',
|
||||||
|
**extra
|
||||||
|
)
|
||||||
|
|
||||||
|
This way the parameters will only be used under Python 3, where you have to
|
||||||
|
use Distribute.
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
=======
|
||||||
|
Roadmap
|
||||||
|
=======
|
||||||
|
|
||||||
|
Distribute has two branches:
|
||||||
|
|
||||||
|
- 0.6.x : provides a Setuptools-0.6cX compatible version
|
||||||
|
- 0.7.x : will provide a refactoring
|
||||||
|
|
||||||
|
0.6.x
|
||||||
|
=====
|
||||||
|
|
||||||
|
Not "much" is going to happen here, we want this branch to be helpful
|
||||||
|
to the community *today* by addressing the 40-or-so bugs
|
||||||
|
that were found in Setuptools and never fixed. This is eventually
|
||||||
|
happen soon because its development is
|
||||||
|
fast : there are up to 5 commiters that are working on it very often
|
||||||
|
(and the number grows weekly.)
|
||||||
|
|
||||||
|
The biggest issue with this branch is that it is providing the same
|
||||||
|
packages and modules setuptools does, and this
|
||||||
|
requires some bootstrapping work where we make sure once Distribute is
|
||||||
|
installed, all Distribution that requires Setuptools
|
||||||
|
will continue to work. This is done by faking the metadata of
|
||||||
|
Setuptools 0.6c9. That's the only way we found to do this.
|
||||||
|
|
||||||
|
There's one major thing though: thanks to the work of Lennart, Alex,
|
||||||
|
Martin, this branch supports Python 3,
|
||||||
|
which is great to have to speed up Py3 adoption.
|
||||||
|
|
||||||
|
The goal of the 0.6.x is to remove as much bugs as we can, and try if
|
||||||
|
possible to remove the patches done
|
||||||
|
on Distutils. We will support 0.6.x maintenance for years and we will
|
||||||
|
promote its usage everywhere instead of
|
||||||
|
Setuptools.
|
||||||
|
|
||||||
|
Some new commands are added there, when they are helpful and don't
|
||||||
|
interact with the rest. I am thinking
|
||||||
|
about "upload_docs" that let you upload documentation to PyPI. The
|
||||||
|
goal is to move it to Distutils
|
||||||
|
at some point, if the documentation feature of PyPI stays and starts to be used.
|
||||||
|
|
||||||
|
0.7.x
|
||||||
|
=====
|
||||||
|
|
||||||
|
We've started to refactor Distribute with this roadmap in mind (and
|
||||||
|
no, as someone said, it's not vaporware,
|
||||||
|
we've done a lot already)
|
||||||
|
|
||||||
|
- 0.7.x can be installed and used with 0.6.x
|
||||||
|
|
||||||
|
- easy_install is going to be deprecated ! use Pip !
|
||||||
|
|
||||||
|
- the version system will be deprecated, in favor of the one in Distutils
|
||||||
|
|
||||||
|
- no more Distutils monkey-patch that happens once you use the code
|
||||||
|
(things like 'from distutils import cmd; cmd.Command = CustomCommand')
|
||||||
|
|
||||||
|
- no more custom site.py (that is: if something misses in Python's
|
||||||
|
site.py we'll add it there instead of patching it)
|
||||||
|
|
||||||
|
- no more namespaced packages system, if PEP 382 (namespaces package
|
||||||
|
support) makes it to 2.7
|
||||||
|
|
||||||
|
- The code is splitted in many packages and might be distributed under
|
||||||
|
several distributions.
|
||||||
|
|
||||||
|
- distribute.resources: that's the old pkg_resources, but
|
||||||
|
reorganized in clean, pep-8 modules. This package will
|
||||||
|
only contain the query APIs and will focus on being PEP 376
|
||||||
|
compatible. We will promote its usage and see if Pip wants
|
||||||
|
to use it as a basis.
|
||||||
|
It will probably shrink a lot though, once the stdlib provides PEP 376 support.
|
||||||
|
|
||||||
|
- distribute.entrypoints: that's the old pkg_resources entry points
|
||||||
|
system, but on its own. it uses distribute.resources
|
||||||
|
|
||||||
|
- distribute.index: that's package_index and a few other things.
|
||||||
|
everything required to interact with PyPI. We will promote
|
||||||
|
its usage and see if Pip wants to use it as a basis.
|
||||||
|
|
||||||
|
- distribute.core (might be renamed to main): that's everything
|
||||||
|
else, and uses the other packages.
|
||||||
|
|
||||||
|
Goal: A first release before (or when) Python 2.7 / 3.2 is out.
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
================================
|
||||||
|
Using Distribute in your project
|
||||||
|
================================
|
||||||
|
|
||||||
|
To use Distribute in your project, the recommended way is to ship
|
||||||
|
`distribute_setup.py` alongside your `setup.py` script and call
|
||||||
|
it at the very begining of `setup.py` like this::
|
||||||
|
|
||||||
|
from distribute_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
|
||||||
|
Another way is to add ``Distribute`` in the ``install_requires`` option::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
install_requires=['distribute']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
XXX to be finished
|
||||||
@@ -0,0 +1,540 @@
|
|||||||
|
/*
|
||||||
|
* basic.css
|
||||||
|
* ~~~~~~~~~
|
||||||
|
*
|
||||||
|
* Sphinx stylesheet -- basic theme.
|
||||||
|
*
|
||||||
|
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||||
|
* :license: BSD, see LICENSE for details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* -- main layout ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
div.clearer {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- relbar ---------------------------------------------------------------- */
|
||||||
|
|
||||||
|
div.related {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related h3 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related li.right {
|
||||||
|
float: right;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- sidebar --------------------------------------------------------------- */
|
||||||
|
|
||||||
|
div.sphinxsidebarwrapper {
|
||||||
|
padding: 10px 5px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar {
|
||||||
|
float: left;
|
||||||
|
width: 230px;
|
||||||
|
margin-left: -100%;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar ul ul,
|
||||||
|
div.sphinxsidebar ul.want-points {
|
||||||
|
margin-left: 20px;
|
||||||
|
list-style: square;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar ul ul {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar form {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar input {
|
||||||
|
border: 1px solid #98dbcc;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar #searchbox input[type="text"] {
|
||||||
|
width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar #searchbox input[type="submit"] {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- search page ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
ul.search {
|
||||||
|
margin: 10px 0 0 20px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.search li {
|
||||||
|
padding: 5px 0 5px 20px;
|
||||||
|
background-image: url(file.png);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 0 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.search li a {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.search li div.context {
|
||||||
|
color: #888;
|
||||||
|
margin: 2px 0 0 30px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.keywordmatches li.goodmatch a {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- index page ------------------------------------------------------------ */
|
||||||
|
|
||||||
|
table.contentstable {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.contentstable p.biglink {
|
||||||
|
line-height: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.biglink {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.linkdescr {
|
||||||
|
font-style: italic;
|
||||||
|
padding-top: 5px;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- general index --------------------------------------------------------- */
|
||||||
|
|
||||||
|
table.indextable {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.indextable td {
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.indextable dl, table.indextable dd {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.indextable tr.pcap {
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.indextable tr.cap {
|
||||||
|
margin-top: 10px;
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.toggler {
|
||||||
|
margin-right: 3px;
|
||||||
|
margin-top: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.modindex-jumpbox {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
margin: 1em 0 1em 0;
|
||||||
|
padding: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.genindex-jumpbox {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
margin: 1em 0 1em 0;
|
||||||
|
padding: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- general body styles --------------------------------------------------- */
|
||||||
|
|
||||||
|
a.headerlink {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:hover > a.headerlink,
|
||||||
|
h2:hover > a.headerlink,
|
||||||
|
h3:hover > a.headerlink,
|
||||||
|
h4:hover > a.headerlink,
|
||||||
|
h5:hover > a.headerlink,
|
||||||
|
h6:hover > a.headerlink,
|
||||||
|
dt:hover > a.headerlink {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body p.caption {
|
||||||
|
text-align: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body td {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-list ul {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.first {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.rubric {
|
||||||
|
margin-top: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.align-left, .figure.align-left, object.align-left {
|
||||||
|
clear: left;
|
||||||
|
float: left;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.align-right, .figure.align-right, object.align-right {
|
||||||
|
clear: right;
|
||||||
|
float: right;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.align-center, .figure.align-center, object.align-center {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- sidebars -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
div.sidebar {
|
||||||
|
margin: 0 0 0.5em 1em;
|
||||||
|
border: 1px solid #ddb;
|
||||||
|
padding: 7px 7px 0 7px;
|
||||||
|
background-color: #ffe;
|
||||||
|
width: 40%;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.sidebar-title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- topics ---------------------------------------------------------------- */
|
||||||
|
|
||||||
|
div.topic {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 7px 7px 0 7px;
|
||||||
|
margin: 10px 0 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.topic-title {
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- admonitions ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
div.admonition {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.admonition dt {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.admonition dl {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.admonition-title {
|
||||||
|
margin: 0px 10px 5px 0px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body p.centered {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- tables ---------------------------------------------------------------- */
|
||||||
|
|
||||||
|
table.docutils {
|
||||||
|
border: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.docutils td, table.docutils th {
|
||||||
|
padding: 1px 8px 1px 5px;
|
||||||
|
border-top: 0;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
|
border-bottom: 1px solid #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.field-list td, table.field-list th {
|
||||||
|
border: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.footnote td, table.footnote th {
|
||||||
|
border: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.citation {
|
||||||
|
border-left: solid 1px gray;
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.citation td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- other body styles ----------------------------------------------------- */
|
||||||
|
|
||||||
|
ol.arabic {
|
||||||
|
list-style: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol.loweralpha {
|
||||||
|
list-style: lower-alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol.upperalpha {
|
||||||
|
list-style: upper-alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol.lowerroman {
|
||||||
|
list-style: lower-roman;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol.upperroman {
|
||||||
|
list-style: upper-roman;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd p {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd ul, dd table {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-top: 3px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt:target, .highlighted {
|
||||||
|
background-color: #fbe54e;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl.glossary dt {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-list ul {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-list p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refcount {
|
||||||
|
color: #060;
|
||||||
|
}
|
||||||
|
|
||||||
|
.optional {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.versionmodified {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-message {
|
||||||
|
background-color: #fda;
|
||||||
|
padding: 5px;
|
||||||
|
border: 3px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnote:target {
|
||||||
|
background-color: #ffa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-block {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-block .line-block {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guilabel, .menuselection {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accelerator {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.classifier {
|
||||||
|
font-style: oblique;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr, acronym {
|
||||||
|
border-bottom: dotted 1px;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- code displays --------------------------------------------------------- */
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
||||||
|
}
|
||||||
|
|
||||||
|
td.linenos pre {
|
||||||
|
padding: 5px 0px;
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.highlighttable {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.highlighttable td {
|
||||||
|
padding: 0 0.5em 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt.descname {
|
||||||
|
background-color: transparent;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt.descclassname {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt.xref, a tt {
|
||||||
|
background-color: transparent;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewcode-link {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewcode-back {
|
||||||
|
float: right;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.viewcode-block:target {
|
||||||
|
margin: -1px -10px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- math display ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
img.math {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body div.math p {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.eqno {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- printout stylesheet --------------------------------------------------- */
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
div.document,
|
||||||
|
div.documentwrapper,
|
||||||
|
div.bodywrapper {
|
||||||
|
margin: 0 !important;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar,
|
||||||
|
div.related,
|
||||||
|
div.footer,
|
||||||
|
#top-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
* Sphinx stylesheet -- default theme
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import url("basic.css");
|
||||||
|
|
||||||
|
/* -- page layout ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: 100%;
|
||||||
|
background-color: #111111;
|
||||||
|
color: #555555;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.documentwrapper {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.bodywrapper {
|
||||||
|
margin: 0 0 0 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr{
|
||||||
|
border: 1px solid #B1B4B6;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.document {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #3E4349;
|
||||||
|
padding: 1em 30px 30px 30px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.footer {
|
||||||
|
color: #555;
|
||||||
|
width: 100%;
|
||||||
|
padding: 13px 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.footer a {
|
||||||
|
color: #444444;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related {
|
||||||
|
background-color: #6BA81E;
|
||||||
|
line-height: 36px;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0px 1px 0 #444444;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related a {
|
||||||
|
color: #E2F3CC;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related .right {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar {
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebarwrapper{
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar h3,
|
||||||
|
div.sphinxsidebar h4 {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
color: #222222;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px 10px;
|
||||||
|
text-shadow: 1px 1px 0 white
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar h3 a {
|
||||||
|
color: #444444;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar p {
|
||||||
|
color: #888888;
|
||||||
|
padding: 5px 20px;
|
||||||
|
margin: 0.5em 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar p.topless {
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar ul {
|
||||||
|
margin: 10px 10px 10px 20px;
|
||||||
|
padding: 0;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar a {
|
||||||
|
color: #444444;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar a:hover {
|
||||||
|
color: #E32E00;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar input {
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 1.1em;
|
||||||
|
padding: 0.15em 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar input[type=text]{
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- body styles ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #005B81;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #E32E00;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body h1,
|
||||||
|
div.body h2,
|
||||||
|
div.body h3,
|
||||||
|
div.body h4,
|
||||||
|
div.body h5,
|
||||||
|
div.body h6 {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #212224;
|
||||||
|
margin: 30px 0px 10px 0px;
|
||||||
|
padding: 5px 0 5px 0px;
|
||||||
|
text-shadow: 0px 1px 0 white;
|
||||||
|
border-bottom: 1px solid #C8D5E3;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body h1 { margin-top: 0; font-size: 200%; }
|
||||||
|
div.body h2 { font-size: 150%; }
|
||||||
|
div.body h3 { font-size: 120%; }
|
||||||
|
div.body h4 { font-size: 110%; }
|
||||||
|
div.body h5 { font-size: 100%; }
|
||||||
|
div.body h6 { font-size: 100%; }
|
||||||
|
|
||||||
|
a.headerlink {
|
||||||
|
color: #c60f0f;
|
||||||
|
font-size: 0.8em;
|
||||||
|
padding: 0 4px 0 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.headerlink:hover {
|
||||||
|
background-color: #c60f0f;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body p, div.body dd, div.body li {
|
||||||
|
line-height: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.admonition p.admonition-title + p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.highlight{
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.note {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.seealso {
|
||||||
|
background-color: #ffffcc;
|
||||||
|
border: 1px solid #ffff66;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.topic {
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.warning {
|
||||||
|
background-color: #ffe4e4;
|
||||||
|
border: 1px solid #ff6666;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.admonition-title {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.admonition-title:after {
|
||||||
|
content: ":";
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
color: #222222;
|
||||||
|
line-height: 1.5em;
|
||||||
|
font-size: 1.1em;
|
||||||
|
margin: 1.5em 0 1.5em 0;
|
||||||
|
-webkit-box-shadow: 0px 0px 4px #d8d8d8;
|
||||||
|
-moz-box-shadow: 0px 0px 4px #d8d8d8;
|
||||||
|
box-shadow: 0px 0px 4px #d8d8d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt {
|
||||||
|
color: #222222;
|
||||||
|
padding: 1px 2px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#table-of-contents ul {
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
.c { color: #999988; font-style: italic } /* Comment */
|
||||||
|
.k { font-weight: bold } /* Keyword */
|
||||||
|
.o { font-weight: bold } /* Operator */
|
||||||
|
.cm { color: #999988; font-style: italic } /* Comment.Multiline */
|
||||||
|
.cp { color: #999999; font-weight: bold } /* Comment.preproc */
|
||||||
|
.c1 { color: #999988; font-style: italic } /* Comment.Single */
|
||||||
|
.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
|
||||||
|
.ge { font-style: italic } /* Generic.Emph */
|
||||||
|
.gr { color: #aa0000 } /* Generic.Error */
|
||||||
|
.gh { color: #999999 } /* Generic.Heading */
|
||||||
|
.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
|
||||||
|
.go { color: #111 } /* Generic.Output */
|
||||||
|
.gp { color: #555555 } /* Generic.Prompt */
|
||||||
|
.gs { font-weight: bold } /* Generic.Strong */
|
||||||
|
.gu { color: #aaaaaa } /* Generic.Subheading */
|
||||||
|
.gt { color: #aa0000 } /* Generic.Traceback */
|
||||||
|
.kc { font-weight: bold } /* Keyword.Constant */
|
||||||
|
.kd { font-weight: bold } /* Keyword.Declaration */
|
||||||
|
.kp { font-weight: bold } /* Keyword.Pseudo */
|
||||||
|
.kr { font-weight: bold } /* Keyword.Reserved */
|
||||||
|
.kt { color: #445588; font-weight: bold } /* Keyword.Type */
|
||||||
|
.m { color: #009999 } /* Literal.Number */
|
||||||
|
.s { color: #bb8844 } /* Literal.String */
|
||||||
|
.na { color: #008080 } /* Name.Attribute */
|
||||||
|
.nb { color: #999999 } /* Name.Builtin */
|
||||||
|
.nc { color: #445588; font-weight: bold } /* Name.Class */
|
||||||
|
.no { color: #ff99ff } /* Name.Constant */
|
||||||
|
.ni { color: #800080 } /* Name.Entity */
|
||||||
|
.ne { color: #990000; font-weight: bold } /* Name.Exception */
|
||||||
|
.nf { color: #990000; font-weight: bold } /* Name.Function */
|
||||||
|
.nn { color: #555555 } /* Name.Namespace */
|
||||||
|
.nt { color: #000080 } /* Name.Tag */
|
||||||
|
.nv { color: purple } /* Name.Variable */
|
||||||
|
.ow { font-weight: bold } /* Operator.Word */
|
||||||
|
.mf { color: #009999 } /* Literal.Number.Float */
|
||||||
|
.mh { color: #009999 } /* Literal.Number.Hex */
|
||||||
|
.mi { color: #009999 } /* Literal.Number.Integer */
|
||||||
|
.mo { color: #009999 } /* Literal.Number.Oct */
|
||||||
|
.sb { color: #bb8844 } /* Literal.String.Backtick */
|
||||||
|
.sc { color: #bb8844 } /* Literal.String.Char */
|
||||||
|
.sd { color: #bb8844 } /* Literal.String.Doc */
|
||||||
|
.s2 { color: #bb8844 } /* Literal.String.Double */
|
||||||
|
.se { color: #bb8844 } /* Literal.String.Escape */
|
||||||
|
.sh { color: #bb8844 } /* Literal.String.Heredoc */
|
||||||
|
.si { color: #bb8844 } /* Literal.String.Interpol */
|
||||||
|
.sx { color: #bb8844 } /* Literal.String.Other */
|
||||||
|
.sr { color: #808000 } /* Literal.String.Regex */
|
||||||
|
.s1 { color: #bb8844 } /* Literal.String.Single */
|
||||||
|
.ss { color: #bb8844 } /* Literal.String.Symbol */
|
||||||
|
.bp { color: #999999 } /* Name.Builtin.Pseudo */
|
||||||
|
.vc { color: #ff99ff } /* Name.Variable.Class */
|
||||||
|
.vg { color: #ff99ff } /* Name.Variable.Global */
|
||||||
|
.vi { color: #ff99ff } /* Name.Variable.Instance */
|
||||||
|
.il { color: #009999 } /* Literal.Number.Integer.Long */
|
||||||
+197
@@ -0,0 +1,197 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Distribute documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Fri Jul 17 14:22:37 2009.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
#
|
||||||
|
# The contents of this file are pickled, so don't put values in the namespace
|
||||||
|
# that aren't pickleable (module imports are okay, they're removed automatically).
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#sys.path.append(os.path.abspath('.'))
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = []
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.txt'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'Distribute'
|
||||||
|
copyright = u'2009-2011, The fellowship of the packaging'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '0.6.34'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.6.34'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#language = None
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of documents that shouldn't be included in the build.
|
||||||
|
#unused_docs = []
|
||||||
|
|
||||||
|
# List of directories, relative to source directory, that shouldn't be searched
|
||||||
|
# for source files.
|
||||||
|
exclude_trees = []
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
#add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||||
|
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||||
|
html_theme = 'nature'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
html_theme_path = ['_theme']
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
html_title = "Distribute documentation"
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
html_short_title = "Distribute"
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
#html_static_path = ['_static']
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
html_sidebars = {'index': 'indexsidebar.html'}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
html_use_modindex = False
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
html_use_index = False
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = ''
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'Distributedoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
# The paper size ('letter' or 'a4').
|
||||||
|
#latex_paper_size = 'letter'
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#latex_font_size = '10pt'
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'Distribute.tex', ur'Distribute Documentation',
|
||||||
|
ur'The fellowship of the packaging', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
# the title page.
|
||||||
|
#latex_logo = None
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
|
# not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#latex_preamble = ''
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_use_modindex = True
|
||||||
+1597
File diff suppressed because it is too large
Load Diff
+36
@@ -0,0 +1,36 @@
|
|||||||
|
Welcome to Distribute's documentation!
|
||||||
|
======================================
|
||||||
|
|
||||||
|
`Distribute` is a fork of the `Setuptools` project.
|
||||||
|
|
||||||
|
Distribute is intended to replace Setuptools as the standard method for
|
||||||
|
working with Python module distributions.
|
||||||
|
|
||||||
|
For those who may wonder why they should switch to Distribute over Setuptools, it’s quite simple:
|
||||||
|
|
||||||
|
- Distribute is a drop-in replacement for Setuptools
|
||||||
|
- The code is actively maintained, and has over 10 commiters
|
||||||
|
- Distribute offers Python 3 support !
|
||||||
|
|
||||||
|
Documentation content:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
roadmap
|
||||||
|
python3
|
||||||
|
using
|
||||||
|
setuptools
|
||||||
|
easy_install
|
||||||
|
pkg_resources
|
||||||
|
|
||||||
|
|
||||||
|
.. image:: http://python-distribute.org/pip_distribute.png
|
||||||
|
|
||||||
|
Design done by Idan Gazit (http://pixane.com) - License: cc-by-3.0
|
||||||
|
|
||||||
|
Copy & paste::
|
||||||
|
|
||||||
|
curl -O http://python-distribute.org/distribute_setup.py
|
||||||
|
python distribute_setup.py
|
||||||
|
easy_install pip
|
||||||
+1955
File diff suppressed because it is too large
Load Diff
+121
@@ -0,0 +1,121 @@
|
|||||||
|
=====================================================
|
||||||
|
Supporting both Python 2 and Python 3 with Distribute
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
Starting with version 0.6.2, Distribute supports Python 3. Installing and
|
||||||
|
using distribute for Python 3 code works exactly the same as for Python 2
|
||||||
|
code, but Distribute also helps you to support Python 2 and Python 3 from
|
||||||
|
the same source code by letting you run 2to3 on the code as a part of the
|
||||||
|
build process, by setting the keyword parameter ``use_2to3`` to True.
|
||||||
|
|
||||||
|
|
||||||
|
Distribute as help during porting
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Distribute can make the porting process much easier by automatically running
|
||||||
|
2to3 as a part of the test running. To do this you need to configure the
|
||||||
|
setup.py so that you can run the unit tests with ``python setup.py test``.
|
||||||
|
|
||||||
|
See :ref:`test` for more information on this.
|
||||||
|
|
||||||
|
Once you have the tests running under Python 2, you can add the use_2to3
|
||||||
|
keyword parameters to setup(), and start running the tests under Python 3.
|
||||||
|
The test command will now first run the build command during which the code
|
||||||
|
will be converted with 2to3, and the tests will then be run from the build
|
||||||
|
directory, as opposed from the source directory as is normally done.
|
||||||
|
|
||||||
|
Distribute will convert all Python files, and also all doctests in Python
|
||||||
|
files. However, if you have doctests located in separate text files, these
|
||||||
|
will not automatically be converted. By adding them to the
|
||||||
|
``convert_2to3_doctests`` keyword parameter Distrubute will convert them as
|
||||||
|
well.
|
||||||
|
|
||||||
|
By default, the conversion uses all fixers in the ``lib2to3.fixers`` package.
|
||||||
|
To use additional fixers, the parameter ``use_2to3_fixers`` can be set
|
||||||
|
to a list of names of packages containing fixers. To exclude fixers, the
|
||||||
|
parameter ``use_2to3_exclude_fixers`` can be set to fixer names to be
|
||||||
|
skipped.
|
||||||
|
|
||||||
|
A typical setup.py can look something like this::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='your.module',
|
||||||
|
version = '1.0',
|
||||||
|
description='This is your awesome module',
|
||||||
|
author='You',
|
||||||
|
author_email='your@email',
|
||||||
|
package_dir = {'': 'src'},
|
||||||
|
packages = ['your', 'you.module'],
|
||||||
|
test_suite = 'your.module.tests',
|
||||||
|
use_2to3 = True,
|
||||||
|
convert_2to3_doctests = ['src/your/module/README.txt'],
|
||||||
|
use_2to3_fixers = ['your.fixers'],
|
||||||
|
use_2to3_exclude_fixers = ['lib2to3.fixes.fix_import'],
|
||||||
|
)
|
||||||
|
|
||||||
|
Differential conversion
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Note that a file will only be copied and converted during the build process
|
||||||
|
if the source file has been changed. If you add a file to the doctests
|
||||||
|
that should be converted, it will not be converted the next time you run
|
||||||
|
the tests, since it hasn't been modified. You need to remove it from the
|
||||||
|
build directory. Also if you run the build, install or test commands before
|
||||||
|
adding the use_2to3 parameter, you will have to remove the build directory
|
||||||
|
before you run the test command, as the files otherwise will seem updated,
|
||||||
|
and no conversion will happen.
|
||||||
|
|
||||||
|
In general, if code doesn't seem to be converted, deleting the build directory
|
||||||
|
and trying again is a good saferguard against the build directory getting
|
||||||
|
"out of sync" with the source directory.
|
||||||
|
|
||||||
|
Distributing Python 3 modules
|
||||||
|
=============================
|
||||||
|
|
||||||
|
You can distribute your modules with Python 3 support in different ways. A
|
||||||
|
normal source distribution will work, but can be slow in installing, as the
|
||||||
|
2to3 process will be run during the install. But you can also distribute
|
||||||
|
the module in binary format, such as a binary egg. That egg will contain the
|
||||||
|
already converted code, and hence no 2to3 conversion is needed during install.
|
||||||
|
|
||||||
|
Advanced features
|
||||||
|
=================
|
||||||
|
|
||||||
|
If you don't want to run the 2to3 conversion on the doctests in Python files,
|
||||||
|
you can turn that off by setting ``setuptools.use_2to3_on_doctests = False``.
|
||||||
|
|
||||||
|
Note on compatibility with setuptools
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Setuptools do not know about the new keyword parameters to support Python 3.
|
||||||
|
As a result it will warn about the unknown keyword parameters if you use
|
||||||
|
setuptools instead of Distribute under Python 2. This is not an error, and
|
||||||
|
install process will continue as normal, but if you want to get rid of that
|
||||||
|
error this is easy. Simply conditionally add the new parameters into an extra
|
||||||
|
dict and pass that dict into setup()::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
import sys
|
||||||
|
|
||||||
|
extra = {}
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
extra['use_2to3'] = True
|
||||||
|
extra['convert_2to3_doctests'] = ['src/your/module/README.txt']
|
||||||
|
extra['use_2to3_fixers'] = ['your.fixers']
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='your.module',
|
||||||
|
version = '1.0',
|
||||||
|
description='This is your awesome module',
|
||||||
|
author='You',
|
||||||
|
author_email='your@email',
|
||||||
|
package_dir = {'': 'src'},
|
||||||
|
packages = ['your', 'you.module'],
|
||||||
|
test_suite = 'your.module.tests',
|
||||||
|
**extra
|
||||||
|
)
|
||||||
|
|
||||||
|
This way the parameters will only be used under Python 3, where you have to
|
||||||
|
use Distribute.
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
=======
|
||||||
|
Roadmap
|
||||||
|
=======
|
||||||
|
|
||||||
|
Distribute has two branches:
|
||||||
|
|
||||||
|
- 0.6.x : provides a Setuptools-0.6cX compatible version
|
||||||
|
- 0.7.x : will provide a refactoring
|
||||||
|
|
||||||
|
0.6.x
|
||||||
|
=====
|
||||||
|
|
||||||
|
Not "much" is going to happen here, we want this branch to be helpful
|
||||||
|
to the community *today* by addressing the 40-or-so bugs
|
||||||
|
that were found in Setuptools and never fixed. This is eventually
|
||||||
|
happen soon because its development is
|
||||||
|
fast : there are up to 5 commiters that are working on it very often
|
||||||
|
(and the number grows weekly.)
|
||||||
|
|
||||||
|
The biggest issue with this branch is that it is providing the same
|
||||||
|
packages and modules setuptools does, and this
|
||||||
|
requires some bootstrapping work where we make sure once Distribute is
|
||||||
|
installed, all Distribution that requires Setuptools
|
||||||
|
will continue to work. This is done by faking the metadata of
|
||||||
|
Setuptools 0.6c9. That's the only way we found to do this.
|
||||||
|
|
||||||
|
There's one major thing though: thanks to the work of Lennart, Alex,
|
||||||
|
Martin, this branch supports Python 3,
|
||||||
|
which is great to have to speed up Py3 adoption.
|
||||||
|
|
||||||
|
The goal of the 0.6.x is to remove as much bugs as we can, and try if
|
||||||
|
possible to remove the patches done
|
||||||
|
on Distutils. We will support 0.6.x maintenance for years and we will
|
||||||
|
promote its usage everywhere instead of
|
||||||
|
Setuptools.
|
||||||
|
|
||||||
|
Some new commands are added there, when they are helpful and don't
|
||||||
|
interact with the rest. I am thinking
|
||||||
|
about "upload_docs" that let you upload documentation to PyPI. The
|
||||||
|
goal is to move it to Distutils
|
||||||
|
at some point, if the documentation feature of PyPI stays and starts to be used.
|
||||||
|
|
||||||
|
0.7.x
|
||||||
|
=====
|
||||||
|
|
||||||
|
We've started to refactor Distribute with this roadmap in mind (and
|
||||||
|
no, as someone said, it's not vaporware,
|
||||||
|
we've done a lot already)
|
||||||
|
|
||||||
|
- 0.7.x can be installed and used with 0.6.x
|
||||||
|
|
||||||
|
- easy_install is going to be deprecated ! use Pip !
|
||||||
|
|
||||||
|
- the version system will be deprecated, in favor of the one in Distutils
|
||||||
|
|
||||||
|
- no more Distutils monkey-patch that happens once you use the code
|
||||||
|
(things like 'from distutils import cmd; cmd.Command = CustomCommand')
|
||||||
|
|
||||||
|
- no more custom site.py (that is: if something misses in Python's
|
||||||
|
site.py we'll add it there instead of patching it)
|
||||||
|
|
||||||
|
- no more namespaced packages system, if PEP 382 (namespaces package
|
||||||
|
support) makes it to 2.7
|
||||||
|
|
||||||
|
- The code is splitted in many packages and might be distributed under
|
||||||
|
several distributions.
|
||||||
|
|
||||||
|
- distribute.resources: that's the old pkg_resources, but
|
||||||
|
reorganized in clean, pep-8 modules. This package will
|
||||||
|
only contain the query APIs and will focus on being PEP 376
|
||||||
|
compatible. We will promote its usage and see if Pip wants
|
||||||
|
to use it as a basis.
|
||||||
|
It will probably shrink a lot though, once the stdlib provides PEP 376 support.
|
||||||
|
|
||||||
|
- distribute.entrypoints: that's the old pkg_resources entry points
|
||||||
|
system, but on its own. it uses distribute.resources
|
||||||
|
|
||||||
|
- distribute.index: that's package_index and a few other things.
|
||||||
|
everything required to interact with PyPI. We will promote
|
||||||
|
its usage and see if Pip wants to use it as a basis.
|
||||||
|
|
||||||
|
- distribute.core (might be renamed to main): that's everything
|
||||||
|
else, and uses the other packages.
|
||||||
|
|
||||||
|
Goal: A first release before (or when) Python 2.7 / 3.2 is out.
|
||||||
|
|
||||||
+3236
File diff suppressed because it is too large
Load Diff
+21
@@ -0,0 +1,21 @@
|
|||||||
|
================================
|
||||||
|
Using Distribute in your project
|
||||||
|
================================
|
||||||
|
|
||||||
|
To use Distribute in your project, the recommended way is to ship
|
||||||
|
`distribute_setup.py` alongside your `setup.py` script and call
|
||||||
|
it at the very begining of `setup.py` like this::
|
||||||
|
|
||||||
|
from distribute_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
|
||||||
|
Another way is to add ``Distribute`` in the ``install_requires`` option::
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(...
|
||||||
|
install_requires=['distribute']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
XXX to be finished
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
"""Run the EasyInstall command"""
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from setuptools.command.easy_install import main
|
||||||
|
main()
|
||||||
Vendored
+327
@@ -0,0 +1,327 @@
|
|||||||
|
/* Setuptools Script Launcher for Windows
|
||||||
|
|
||||||
|
This is a stub executable for Windows that functions somewhat like
|
||||||
|
Effbot's "exemaker", in that it runs a script with the same name but
|
||||||
|
a .py extension, using information from a #! line. It differs in that
|
||||||
|
it spawns the actual Python executable, rather than attempting to
|
||||||
|
hook into the Python DLL. This means that the script will run with
|
||||||
|
sys.executable set to the Python executable, where exemaker ends up with
|
||||||
|
sys.executable pointing to itself. (Which means it won't work if you try
|
||||||
|
to run another Python process using sys.executable.)
|
||||||
|
|
||||||
|
To build/rebuild with mingw32, do this in the setuptools project directory:
|
||||||
|
|
||||||
|
gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c
|
||||||
|
gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c
|
||||||
|
|
||||||
|
It links to msvcrt.dll, but this shouldn't be a problem since it doesn't
|
||||||
|
actually run Python in the same process. Note that using 'exec' instead
|
||||||
|
of 'spawn' doesn't work, because on Windows this leads to the Python
|
||||||
|
executable running in the *background*, attached to the same console
|
||||||
|
window, meaning you get a command prompt back *before* Python even finishes
|
||||||
|
starting. So, we have to use spawnv() and wait for Python to exit before
|
||||||
|
continuing. :(
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
int child_pid=0;
|
||||||
|
|
||||||
|
int fail(char *format, char *data) {
|
||||||
|
/* Print error message to stderr and return 2 */
|
||||||
|
fprintf(stderr, format, data);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *quoted(char *data) {
|
||||||
|
int i, ln = strlen(data), nb;
|
||||||
|
|
||||||
|
/* We allocate twice as much space as needed to deal with worse-case
|
||||||
|
of having to escape everything. */
|
||||||
|
char *result = calloc(ln*2+3, sizeof(char));
|
||||||
|
char *presult = result;
|
||||||
|
|
||||||
|
*presult++ = '"';
|
||||||
|
for (nb=0, i=0; i < ln; i++)
|
||||||
|
{
|
||||||
|
if (data[i] == '\\')
|
||||||
|
nb += 1;
|
||||||
|
else if (data[i] == '"')
|
||||||
|
{
|
||||||
|
for (; nb > 0; nb--)
|
||||||
|
*presult++ = '\\';
|
||||||
|
*presult++ = '\\';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nb = 0;
|
||||||
|
*presult++ = data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; nb > 0; nb--) /* Deal w trailing slashes */
|
||||||
|
*presult++ = '\\';
|
||||||
|
|
||||||
|
*presult++ = '"';
|
||||||
|
*presult++ = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
char *loadable_exe(char *exename) {
|
||||||
|
/* HINSTANCE hPython; DLL handle for python executable */
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
/* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||||
|
if (!hPython) return NULL; */
|
||||||
|
|
||||||
|
/* Return the absolute filename for spawnv */
|
||||||
|
result = calloc(MAX_PATH, sizeof(char));
|
||||||
|
strncpy(result, exename, MAX_PATH);
|
||||||
|
/*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
|
||||||
|
|
||||||
|
FreeLibrary(hPython); */
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *find_exe(char *exename, char *script) {
|
||||||
|
char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
|
||||||
|
char path[_MAX_PATH], c, *result;
|
||||||
|
|
||||||
|
/* convert slashes to backslashes for uniform search below */
|
||||||
|
result = exename;
|
||||||
|
while (c = *result++) if (c=='/') result[-1] = '\\';
|
||||||
|
|
||||||
|
_splitpath(exename, drive, dir, fname, ext);
|
||||||
|
if (drive[0] || dir[0]=='\\') {
|
||||||
|
return loadable_exe(exename); /* absolute path, use directly */
|
||||||
|
}
|
||||||
|
/* Use the script's parent directory, which should be the Python home
|
||||||
|
(This should only be used for bdist_wininst-installed scripts, because
|
||||||
|
easy_install-ed scripts use the absolute path to python[w].exe
|
||||||
|
*/
|
||||||
|
_splitpath(script, drive, dir, fname, ext);
|
||||||
|
result = dir + strlen(dir) -1;
|
||||||
|
if (*result == '\\') result--;
|
||||||
|
while (*result != '\\' && result>=dir) *result-- = 0;
|
||||||
|
_makepath(path, drive, dir, exename, NULL);
|
||||||
|
return loadable_exe(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char **parse_argv(char *cmdline, int *argc)
|
||||||
|
{
|
||||||
|
/* Parse a command line in-place using MS C rules */
|
||||||
|
|
||||||
|
char **result = calloc(strlen(cmdline), sizeof(char *));
|
||||||
|
char *output = cmdline;
|
||||||
|
char c;
|
||||||
|
int nb = 0;
|
||||||
|
int iq = 0;
|
||||||
|
*argc = 0;
|
||||||
|
|
||||||
|
result[0] = output;
|
||||||
|
while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
|
||||||
|
|
||||||
|
do {
|
||||||
|
c = *cmdline++;
|
||||||
|
if (!c || (isspace(c) && !iq)) {
|
||||||
|
while (nb) {*output++ = '\\'; nb--; }
|
||||||
|
*output++ = 0;
|
||||||
|
result[++*argc] = output;
|
||||||
|
if (!c) return result;
|
||||||
|
while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
|
||||||
|
if (!*cmdline) return result; /* avoid empty arg if trailing ws */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == '\\')
|
||||||
|
++nb; /* count \'s */
|
||||||
|
else {
|
||||||
|
if (c == '"') {
|
||||||
|
if (!(nb & 1)) { iq = !iq; c = 0; } /* skip " unless odd # of \ */
|
||||||
|
nb = nb >> 1; /* cut \'s in half */
|
||||||
|
}
|
||||||
|
while (nb) {*output++ = '\\'; nb--; }
|
||||||
|
if (c) *output++ = c;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pass_control_to_child(DWORD control_type) {
|
||||||
|
/*
|
||||||
|
* distribute-issue207
|
||||||
|
* passes the control event to child process (Python)
|
||||||
|
*/
|
||||||
|
if (!child_pid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GenerateConsoleCtrlEvent(child_pid,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL control_handler(DWORD control_type) {
|
||||||
|
/*
|
||||||
|
* distribute-issue207
|
||||||
|
* control event handler callback function
|
||||||
|
*/
|
||||||
|
switch (control_type) {
|
||||||
|
case CTRL_C_EVENT:
|
||||||
|
pass_control_to_child(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_and_wait_for_subprocess(char* command) {
|
||||||
|
/*
|
||||||
|
* distribute-issue207
|
||||||
|
* launches child process (Python)
|
||||||
|
*/
|
||||||
|
DWORD return_value = 0;
|
||||||
|
LPSTR commandline = command;
|
||||||
|
STARTUPINFOA s_info;
|
||||||
|
PROCESS_INFORMATION p_info;
|
||||||
|
ZeroMemory(&p_info, sizeof(p_info));
|
||||||
|
ZeroMemory(&s_info, sizeof(s_info));
|
||||||
|
s_info.cb = sizeof(STARTUPINFO);
|
||||||
|
// set-up control handler callback funciotn
|
||||||
|
SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE);
|
||||||
|
if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) {
|
||||||
|
fprintf(stderr, "failed to create process.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
child_pid = p_info.dwProcessId;
|
||||||
|
// wait for Python to exit
|
||||||
|
WaitForSingleObject(p_info.hProcess, INFINITE);
|
||||||
|
if (!GetExitCodeProcess(p_info.hProcess, &return_value)) {
|
||||||
|
fprintf(stderr, "failed to get exit code from process.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* join_executable_and_args(char *executable, char **args, int argc)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* distribute-issue207
|
||||||
|
* CreateProcess needs a long string of the executable and command-line arguments,
|
||||||
|
* so we need to convert it from the args that was built
|
||||||
|
*/
|
||||||
|
int len,counter;
|
||||||
|
char* cmdline;
|
||||||
|
|
||||||
|
len=strlen(executable)+2;
|
||||||
|
for (counter=1; counter<argc; counter++) {
|
||||||
|
len+=strlen(args[counter])+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdline = (char*)calloc(len, sizeof(char));
|
||||||
|
sprintf(cmdline, "%s", executable);
|
||||||
|
len=strlen(executable);
|
||||||
|
for (counter=1; counter<argc; counter++) {
|
||||||
|
sprintf(cmdline+len, " %s", args[counter]);
|
||||||
|
len+=strlen(args[counter])+1;
|
||||||
|
}
|
||||||
|
return cmdline;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run(int argc, char **argv, int is_gui) {
|
||||||
|
|
||||||
|
char python[256]; /* python executable's filename*/
|
||||||
|
char *pyopt; /* Python option */
|
||||||
|
char script[256]; /* the script's filename */
|
||||||
|
|
||||||
|
int scriptf; /* file descriptor for script file */
|
||||||
|
|
||||||
|
char **newargs, **newargsp, **parsedargs; /* argument array for exec */
|
||||||
|
char *ptr, *end; /* working pointers for string manipulation */
|
||||||
|
char *cmdline;
|
||||||
|
int i, parsedargc; /* loop counter */
|
||||||
|
|
||||||
|
/* compute script name from our .exe name*/
|
||||||
|
GetModuleFileNameA(NULL, script, sizeof(script));
|
||||||
|
end = script + strlen(script);
|
||||||
|
while( end>script && *end != '.')
|
||||||
|
*end-- = '\0';
|
||||||
|
*end-- = '\0';
|
||||||
|
strcat(script, (GUI ? "-script.pyw" : "-script.py"));
|
||||||
|
|
||||||
|
/* figure out the target python executable */
|
||||||
|
|
||||||
|
scriptf = open(script, O_RDONLY);
|
||||||
|
if (scriptf == -1) {
|
||||||
|
return fail("Cannot open %s\n", script);
|
||||||
|
}
|
||||||
|
end = python + read(scriptf, python, sizeof(python));
|
||||||
|
close(scriptf);
|
||||||
|
|
||||||
|
ptr = python-1;
|
||||||
|
while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;}
|
||||||
|
|
||||||
|
*ptr-- = '\0';
|
||||||
|
|
||||||
|
if (strncmp(python, "#!", 2)) {
|
||||||
|
/* default to python.exe if no #! header */
|
||||||
|
strcpy(python, "#!python.exe");
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedargs = parse_argv(python+2, &parsedargc);
|
||||||
|
|
||||||
|
/* Using spawnv() can fail strangely if you e.g. find the Cygwin
|
||||||
|
Python, so we'll make sure Windows can find and load it */
|
||||||
|
|
||||||
|
ptr = find_exe(parsedargs[0], script);
|
||||||
|
if (!ptr) {
|
||||||
|
return fail("Cannot find Python executable %s\n", parsedargs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* printf("Python executable: %s\n", ptr); */
|
||||||
|
|
||||||
|
/* Argument array needs to be
|
||||||
|
parsedargc + argc, plus 1 for null sentinel */
|
||||||
|
|
||||||
|
newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *));
|
||||||
|
newargsp = newargs;
|
||||||
|
|
||||||
|
*newargsp++ = quoted(ptr);
|
||||||
|
for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]);
|
||||||
|
|
||||||
|
*newargsp++ = quoted(script);
|
||||||
|
for (i = 1; i < argc; i++) *newargsp++ = quoted(argv[i]);
|
||||||
|
|
||||||
|
*newargsp++ = NULL;
|
||||||
|
|
||||||
|
/* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */
|
||||||
|
|
||||||
|
if (is_gui) {
|
||||||
|
/* Use exec, we don't need to wait for the GUI to finish */
|
||||||
|
execv(ptr, (const char * const *)(newargs));
|
||||||
|
return fail("Could not exec %s", ptr); /* shouldn't get here! */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* distribute-issue207: using CreateProcessA instead of spawnv
|
||||||
|
*/
|
||||||
|
cmdline = join_executable_and_args(ptr, newargs, parsedargc + argc);
|
||||||
|
return create_and_wait_for_subprocess(cmdline);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) {
|
||||||
|
return run(__argc, __argv, GUI);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
return run(argc, argv, GUI);
|
||||||
|
}
|
||||||
|
|
||||||
+2825
File diff suppressed because it is too large
Load Diff
Vendored
+170
@@ -0,0 +1,170 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Script to fully automate the release process. Requires Python 2.6+
|
||||||
|
with sphinx installed and the 'hg' command on the path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import urllib2
|
||||||
|
import getpass
|
||||||
|
import collections
|
||||||
|
|
||||||
|
try:
|
||||||
|
import keyring
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
VERSION = '0.6.34'
|
||||||
|
|
||||||
|
def get_next_version():
|
||||||
|
digits = map(int, VERSION.split('.'))
|
||||||
|
digits[-1] += 1
|
||||||
|
return '.'.join(map(str, digits))
|
||||||
|
|
||||||
|
NEXT_VERSION = get_next_version()
|
||||||
|
|
||||||
|
files_with_versions = ('docs/conf.py', 'setup.py', 'release.py',
|
||||||
|
'README.txt', 'distribute_setup.py')
|
||||||
|
|
||||||
|
def get_repo_name():
|
||||||
|
"""
|
||||||
|
Get the repo name from the hgrc default path.
|
||||||
|
"""
|
||||||
|
default = subprocess.check_output('hg paths default').strip()
|
||||||
|
parts = default.split('/')
|
||||||
|
if parts[-1] == '':
|
||||||
|
parts.pop()
|
||||||
|
return '/'.join(parts[-2:])
|
||||||
|
|
||||||
|
def get_mercurial_creds(system='https://bitbucket.org', username=None):
|
||||||
|
"""
|
||||||
|
Return named tuple of username,password in much the same way that
|
||||||
|
Mercurial would (from the keyring).
|
||||||
|
"""
|
||||||
|
# todo: consider getting this from .hgrc
|
||||||
|
username = username or getpass.getuser()
|
||||||
|
keyring_username = '@@'.join((username, system))
|
||||||
|
system = '@'.join((keyring_username, 'Mercurial'))
|
||||||
|
password = (
|
||||||
|
keyring.get_password(system, keyring_username)
|
||||||
|
if 'keyring' in globals()
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
if not password:
|
||||||
|
password = getpass.getpass()
|
||||||
|
Credential = collections.namedtuple('Credential', 'username password')
|
||||||
|
return Credential(username, password)
|
||||||
|
|
||||||
|
def add_milestone_and_version(version=NEXT_VERSION):
|
||||||
|
auth = 'Basic ' + ':'.join(get_mercurial_creds()).encode('base64').strip()
|
||||||
|
headers = {
|
||||||
|
'Authorization': auth,
|
||||||
|
}
|
||||||
|
base = 'https://api.bitbucket.org'
|
||||||
|
for type in 'milestones', 'versions':
|
||||||
|
url = (base + '/1.0/repositories/{repo}/issues/{type}'
|
||||||
|
.format(repo = get_repo_name(), type=type))
|
||||||
|
req = urllib2.Request(url = url, headers = headers,
|
||||||
|
data='name='+version)
|
||||||
|
try:
|
||||||
|
urllib2.urlopen(req)
|
||||||
|
except urllib2.HTTPError as e:
|
||||||
|
print(e.fp.read())
|
||||||
|
|
||||||
|
def bump_versions():
|
||||||
|
list(map(bump_version, files_with_versions))
|
||||||
|
|
||||||
|
def bump_version(filename):
|
||||||
|
with open(filename, 'rb') as f:
|
||||||
|
lines = [line.replace(VERSION, NEXT_VERSION) for line in f]
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
|
||||||
|
def do_release():
|
||||||
|
assert all(map(os.path.exists, files_with_versions)), (
|
||||||
|
"Expected file(s) missing")
|
||||||
|
|
||||||
|
assert has_sphinx(), "You must have Sphinx installed to release"
|
||||||
|
|
||||||
|
res = raw_input('Have you read through the SCM changelog and '
|
||||||
|
'confirmed the changelog is current for releasing {VERSION}? '
|
||||||
|
.format(**globals()))
|
||||||
|
if not res.lower().startswith('y'):
|
||||||
|
print("Please do that")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
print("Travis-CI tests: http://travis-ci.org/#!/jaraco/distribute")
|
||||||
|
res = raw_input('Have you or has someone verified that the tests '
|
||||||
|
'pass on this revision? ')
|
||||||
|
if not res.lower().startswith('y'):
|
||||||
|
print("Please do that")
|
||||||
|
raise SystemExit(2)
|
||||||
|
|
||||||
|
subprocess.check_call(['hg', 'tag', VERSION])
|
||||||
|
|
||||||
|
subprocess.check_call(['hg', 'update', VERSION])
|
||||||
|
|
||||||
|
has_docs = build_docs()
|
||||||
|
if os.path.isdir('./dist'):
|
||||||
|
shutil.rmtree('./dist')
|
||||||
|
cmd = [sys.executable, 'setup.py', '-q', 'egg_info', '-RD', '-b', '',
|
||||||
|
'sdist', 'register', 'upload']
|
||||||
|
if has_docs:
|
||||||
|
cmd.append('upload_docs')
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
upload_bootstrap_script()
|
||||||
|
|
||||||
|
# update to the tip for the next operation
|
||||||
|
subprocess.check_call(['hg', 'update'])
|
||||||
|
|
||||||
|
# we just tagged the current version, bump for the next release.
|
||||||
|
bump_versions()
|
||||||
|
subprocess.check_call(['hg', 'ci', '-m',
|
||||||
|
'Bumped to {NEXT_VERSION} in preparation for next '
|
||||||
|
'release.'.format(**globals())])
|
||||||
|
|
||||||
|
# push the changes
|
||||||
|
subprocess.check_call(['hg', 'push'])
|
||||||
|
|
||||||
|
add_milestone_and_version()
|
||||||
|
|
||||||
|
def has_sphinx():
|
||||||
|
try:
|
||||||
|
devnull = open(os.path.devnull, 'wb')
|
||||||
|
subprocess.Popen(['sphinx-build', '--version'], stdout=devnull,
|
||||||
|
stderr=subprocess.STDOUT).wait()
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def build_docs():
|
||||||
|
if not os.path.isdir('docs'):
|
||||||
|
return
|
||||||
|
if os.path.isdir('docs/build'):
|
||||||
|
shutil.rmtree('docs/build')
|
||||||
|
subprocess.check_call([
|
||||||
|
'sphinx-build',
|
||||||
|
'-b', 'html',
|
||||||
|
'-d', 'build/doctrees',
|
||||||
|
'.',
|
||||||
|
'build/html',
|
||||||
|
],
|
||||||
|
cwd='docs')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def upload_bootstrap_script():
|
||||||
|
scp_command = 'pscp' if sys.platform.startswith('win') else 'scp'
|
||||||
|
try:
|
||||||
|
subprocess.check_call([scp_command, 'distribute_setup.py',
|
||||||
|
'pypi@ziade.org:python-distribute.org/'])
|
||||||
|
except:
|
||||||
|
print("Unable to upload bootstrap script. Ask Tarek to do it.")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
do_release()
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
[egg_info]
|
||||||
|
tag_build =
|
||||||
|
tag_svn_revision = 0
|
||||||
|
tag_date = 0
|
||||||
|
|
||||||
|
[aliases]
|
||||||
|
release = egg_info -RDb ''
|
||||||
|
source = register sdist binary
|
||||||
|
binary = bdist_egg upload --show-response
|
||||||
|
|
||||||
|
[build_sphinx]
|
||||||
|
source-dir = docs/
|
||||||
|
build-dir = docs/build
|
||||||
|
all_files = 1
|
||||||
|
|
||||||
|
[upload_docs]
|
||||||
|
upload-dir = docs/build/html
|
||||||
|
|
||||||
|
[sdist]
|
||||||
|
formats = gztar
|
||||||
|
|
||||||
Vendored
+253
@@ -0,0 +1,253 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Distutils setup file, used to install or test 'setuptools'"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import textwrap
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Allow to run setup.py from another directory.
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
src_root = None
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
tmp_src = os.path.join("build", "src")
|
||||||
|
from distutils.filelist import FileList
|
||||||
|
from distutils import dir_util, file_util, util, log
|
||||||
|
log.set_verbosity(1)
|
||||||
|
fl = FileList()
|
||||||
|
manifest_file = open("MANIFEST.in")
|
||||||
|
for line in manifest_file:
|
||||||
|
fl.process_template_line(line)
|
||||||
|
manifest_file.close()
|
||||||
|
dir_util.create_tree(tmp_src, fl.files)
|
||||||
|
outfiles_2to3 = []
|
||||||
|
dist_script = os.path.join("build", "src", "distribute_setup.py")
|
||||||
|
for f in fl.files:
|
||||||
|
outf, copied = file_util.copy_file(f, os.path.join(tmp_src, f), update=1)
|
||||||
|
if copied and outf.endswith(".py") and outf != dist_script:
|
||||||
|
outfiles_2to3.append(outf)
|
||||||
|
if copied and outf.endswith('api_tests.txt'):
|
||||||
|
# XXX support this in distutils as well
|
||||||
|
from lib2to3.main import main
|
||||||
|
main('lib2to3.fixes', ['-wd', os.path.join(tmp_src, 'tests', 'api_tests.txt')])
|
||||||
|
|
||||||
|
util.run_2to3(outfiles_2to3)
|
||||||
|
|
||||||
|
# arrange setup to use the copy
|
||||||
|
sys.path.insert(0, os.path.abspath(tmp_src))
|
||||||
|
src_root = tmp_src
|
||||||
|
|
||||||
|
from distutils.util import convert_path
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
init_path = convert_path('setuptools/command/__init__.py')
|
||||||
|
init_file = open(init_path)
|
||||||
|
exec(init_file.read(), d)
|
||||||
|
init_file.close()
|
||||||
|
|
||||||
|
SETUP_COMMANDS = d['__all__']
|
||||||
|
VERSION = "0.6.34"
|
||||||
|
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
from setuptools.command.build_py import build_py as _build_py
|
||||||
|
from setuptools.command.test import test as _test
|
||||||
|
|
||||||
|
scripts = []
|
||||||
|
|
||||||
|
console_scripts = ["easy_install = setuptools.command.easy_install:main"]
|
||||||
|
if os.environ.get("DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT") is None:
|
||||||
|
console_scripts.append("easy_install-%s = setuptools.command.easy_install:main" % sys.version[:3])
|
||||||
|
|
||||||
|
# specific command that is used to generate windows .exe files
|
||||||
|
class build_py(_build_py):
|
||||||
|
def build_package_data(self):
|
||||||
|
"""Copy data files into build directory"""
|
||||||
|
lastdir = None
|
||||||
|
for package, src_dir, build_dir, filenames in self.data_files:
|
||||||
|
for filename in filenames:
|
||||||
|
target = os.path.join(build_dir, filename)
|
||||||
|
self.mkpath(os.path.dirname(target))
|
||||||
|
srcfile = os.path.join(src_dir, filename)
|
||||||
|
outf, copied = self.copy_file(srcfile, target)
|
||||||
|
srcfile = os.path.abspath(srcfile)
|
||||||
|
|
||||||
|
# avoid a bootstrapping issue with easy_install -U (when the
|
||||||
|
# previous version doesn't have convert_2to3_doctests)
|
||||||
|
if not hasattr(self.distribution, 'convert_2to3_doctests'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if copied and srcfile in self.distribution.convert_2to3_doctests:
|
||||||
|
self.__doctests_2to3.append(outf)
|
||||||
|
|
||||||
|
class test(_test):
|
||||||
|
"""Specific test class to avoid rewriting the entry_points.txt"""
|
||||||
|
def run(self):
|
||||||
|
entry_points = os.path.join('distribute.egg-info', 'entry_points.txt')
|
||||||
|
|
||||||
|
if not os.path.exists(entry_points):
|
||||||
|
_test.run(self)
|
||||||
|
return # even though _test.run will raise SystemExit
|
||||||
|
|
||||||
|
f = open(entry_points)
|
||||||
|
|
||||||
|
# running the test
|
||||||
|
try:
|
||||||
|
ep_content = f.read()
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
try:
|
||||||
|
_test.run(self)
|
||||||
|
finally:
|
||||||
|
# restoring the file
|
||||||
|
f = open(entry_points, 'w')
|
||||||
|
try:
|
||||||
|
f.write(ep_content)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
# if we are installing Distribute using "python setup.py install"
|
||||||
|
# we need to get setuptools out of the way
|
||||||
|
def _easy_install_marker():
|
||||||
|
return (len(sys.argv) == 5 and sys.argv[2] == 'bdist_egg' and
|
||||||
|
sys.argv[3] == '--dist-dir' and 'egg-dist-tmp-' in sys.argv[-1])
|
||||||
|
|
||||||
|
def _buildout_marker():
|
||||||
|
command = os.environ.get('_')
|
||||||
|
if command:
|
||||||
|
return 'buildout' in os.path.basename(command)
|
||||||
|
|
||||||
|
def _being_installed():
|
||||||
|
if os.environ.get('DONT_PATCH_SETUPTOOLS') is not None:
|
||||||
|
return False
|
||||||
|
if _buildout_marker():
|
||||||
|
# Installed by buildout, don't mess with a global setuptools.
|
||||||
|
return False
|
||||||
|
# easy_install marker
|
||||||
|
if "--help" in sys.argv[1:] or "-h" in sys.argv[1:]: # Don't bother doing anything if they're just asking for help
|
||||||
|
return False
|
||||||
|
return 'install' in sys.argv[1:] or _easy_install_marker()
|
||||||
|
|
||||||
|
if _being_installed():
|
||||||
|
from distribute_setup import _before_install
|
||||||
|
_before_install()
|
||||||
|
|
||||||
|
# return contents of reStructureText file with linked issue references
|
||||||
|
def _linkified(rst_path):
|
||||||
|
bitroot = 'http://bitbucket.org/tarek/distribute'
|
||||||
|
revision = re.compile(r'\b(issue\s*#?\d+)\b', re.M | re.I)
|
||||||
|
|
||||||
|
rst_file = open(rst_path)
|
||||||
|
rst_content = rst_file.read()
|
||||||
|
rst_file.close()
|
||||||
|
|
||||||
|
anchors = revision.findall(rst_content) # ['Issue #43', ...]
|
||||||
|
anchors = sorted(set(anchors))
|
||||||
|
rst_content = revision.sub(r'`\1`_', rst_content)
|
||||||
|
rst_content += "\n"
|
||||||
|
for x in anchors:
|
||||||
|
issue = re.findall(r'\d+', x)[0]
|
||||||
|
rst_content += '.. _`%s`: %s/issue/%s\n' % (x, bitroot, issue)
|
||||||
|
rst_content += "\n"
|
||||||
|
return rst_content
|
||||||
|
|
||||||
|
readme_file = open('README.txt')
|
||||||
|
long_description = readme_file.read() + _linkified('CHANGES.txt')
|
||||||
|
readme_file.close()
|
||||||
|
|
||||||
|
dist = setup(
|
||||||
|
name="distribute",
|
||||||
|
version=VERSION,
|
||||||
|
description="Easily download, build, install, upgrade, and uninstall "
|
||||||
|
"Python packages",
|
||||||
|
author="The fellowship of the packaging",
|
||||||
|
author_email="distutils-sig@python.org",
|
||||||
|
license="PSF or ZPL",
|
||||||
|
long_description = long_description,
|
||||||
|
keywords = "CPAN PyPI distutils eggs package management",
|
||||||
|
url = "http://packages.python.org/distribute",
|
||||||
|
test_suite = 'setuptools.tests',
|
||||||
|
src_root = src_root,
|
||||||
|
packages = find_packages(),
|
||||||
|
package_data = {'setuptools':['*.exe']},
|
||||||
|
|
||||||
|
py_modules = ['pkg_resources', 'easy_install', 'site'],
|
||||||
|
|
||||||
|
zip_safe = (sys.version>="2.5"), # <2.5 needs unzipped for -m to work
|
||||||
|
|
||||||
|
cmdclass = {'test': test},
|
||||||
|
entry_points = {
|
||||||
|
|
||||||
|
"distutils.commands" : [
|
||||||
|
"%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals()
|
||||||
|
for cmd in SETUP_COMMANDS
|
||||||
|
],
|
||||||
|
|
||||||
|
"distutils.setup_keywords": [
|
||||||
|
"eager_resources = setuptools.dist:assert_string_list",
|
||||||
|
"namespace_packages = setuptools.dist:check_nsp",
|
||||||
|
"extras_require = setuptools.dist:check_extras",
|
||||||
|
"install_requires = setuptools.dist:check_requirements",
|
||||||
|
"tests_require = setuptools.dist:check_requirements",
|
||||||
|
"entry_points = setuptools.dist:check_entry_points",
|
||||||
|
"test_suite = setuptools.dist:check_test_suite",
|
||||||
|
"zip_safe = setuptools.dist:assert_bool",
|
||||||
|
"package_data = setuptools.dist:check_package_data",
|
||||||
|
"exclude_package_data = setuptools.dist:check_package_data",
|
||||||
|
"include_package_data = setuptools.dist:assert_bool",
|
||||||
|
"packages = setuptools.dist:check_packages",
|
||||||
|
"dependency_links = setuptools.dist:assert_string_list",
|
||||||
|
"test_loader = setuptools.dist:check_importable",
|
||||||
|
"use_2to3 = setuptools.dist:assert_bool",
|
||||||
|
"convert_2to3_doctests = setuptools.dist:assert_string_list",
|
||||||
|
"use_2to3_fixers = setuptools.dist:assert_string_list",
|
||||||
|
"use_2to3_exclude_fixers = setuptools.dist:assert_string_list",
|
||||||
|
],
|
||||||
|
|
||||||
|
"egg_info.writers": [
|
||||||
|
"PKG-INFO = setuptools.command.egg_info:write_pkg_info",
|
||||||
|
"requires.txt = setuptools.command.egg_info:write_requirements",
|
||||||
|
"entry_points.txt = setuptools.command.egg_info:write_entries",
|
||||||
|
"eager_resources.txt = setuptools.command.egg_info:overwrite_arg",
|
||||||
|
"namespace_packages.txt = setuptools.command.egg_info:overwrite_arg",
|
||||||
|
"top_level.txt = setuptools.command.egg_info:write_toplevel_names",
|
||||||
|
"depends.txt = setuptools.command.egg_info:warn_depends_obsolete",
|
||||||
|
"dependency_links.txt = setuptools.command.egg_info:overwrite_arg",
|
||||||
|
],
|
||||||
|
|
||||||
|
"console_scripts": console_scripts,
|
||||||
|
|
||||||
|
"setuptools.file_finders":
|
||||||
|
["svn_cvs = setuptools.command.sdist:_default_revctrl"],
|
||||||
|
|
||||||
|
"setuptools.installation":
|
||||||
|
['eggsecutable = setuptools.command.easy_install:bootstrap'],
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
classifiers = textwrap.dedent("""
|
||||||
|
Development Status :: 5 - Production/Stable
|
||||||
|
Intended Audience :: Developers
|
||||||
|
License :: OSI Approved :: Python Software Foundation License
|
||||||
|
License :: OSI Approved :: Zope Public License
|
||||||
|
Operating System :: OS Independent
|
||||||
|
Programming Language :: Python :: 2.4
|
||||||
|
Programming Language :: Python :: 2.5
|
||||||
|
Programming Language :: Python :: 2.6
|
||||||
|
Programming Language :: Python :: 2.7
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Programming Language :: Python :: 3.1
|
||||||
|
Programming Language :: Python :: 3.2
|
||||||
|
Programming Language :: Python :: 3.3
|
||||||
|
Topic :: Software Development :: Libraries :: Python Modules
|
||||||
|
Topic :: System :: Archiving :: Packaging
|
||||||
|
Topic :: System :: Systems Administration
|
||||||
|
Topic :: Utilities
|
||||||
|
""").strip().splitlines(),
|
||||||
|
scripts = scripts,
|
||||||
|
)
|
||||||
|
|
||||||
|
if _being_installed():
|
||||||
|
from distribute_setup import _after_install
|
||||||
|
_after_install(dist)
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
"""Extensions to the 'distutils' for large or complex distributions"""
|
||||||
|
from setuptools.extension import Extension, Library
|
||||||
|
from setuptools.dist import Distribution, Feature, _get_unpatched
|
||||||
|
import distutils.core, setuptools.command
|
||||||
|
from setuptools.depends import Require
|
||||||
|
from distutils.core import Command as _Command
|
||||||
|
from distutils.util import convert_path
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
__version__ = '0.6'
|
||||||
|
__all__ = [
|
||||||
|
'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require',
|
||||||
|
'find_packages'
|
||||||
|
]
|
||||||
|
|
||||||
|
# This marker is used to simplify the process that checks is the
|
||||||
|
# setuptools package was installed by the Setuptools project
|
||||||
|
# or by the Distribute project, in case Setuptools creates
|
||||||
|
# a distribution with the same version.
|
||||||
|
#
|
||||||
|
# The distribute_setup script for instance, will check if this
|
||||||
|
# attribute is present to decide whether to reinstall the package
|
||||||
|
# or not.
|
||||||
|
_distribute = True
|
||||||
|
|
||||||
|
bootstrap_install_from = None
|
||||||
|
|
||||||
|
# If we run 2to3 on .py files, should we also convert docstrings?
|
||||||
|
# Default: yes; assume that we can detect doctests reliably
|
||||||
|
run_2to3_on_doctests = True
|
||||||
|
# Standard package names for fixer packages
|
||||||
|
lib2to3_fixer_packages = ['lib2to3.fixes']
|
||||||
|
|
||||||
|
def find_packages(where='.', exclude=()):
|
||||||
|
"""Return a list all Python packages found within directory 'where'
|
||||||
|
|
||||||
|
'where' should be supplied as a "cross-platform" (i.e. URL-style) path; it
|
||||||
|
will be converted to the appropriate local path syntax. 'exclude' is a
|
||||||
|
sequence of package names to exclude; '*' can be used as a wildcard in the
|
||||||
|
names, such that 'foo.*' will exclude all subpackages of 'foo' (but not
|
||||||
|
'foo' itself).
|
||||||
|
"""
|
||||||
|
out = []
|
||||||
|
stack=[(convert_path(where), '')]
|
||||||
|
while stack:
|
||||||
|
where,prefix = stack.pop(0)
|
||||||
|
for name in os.listdir(where):
|
||||||
|
fn = os.path.join(where,name)
|
||||||
|
if ('.' not in name and os.path.isdir(fn) and
|
||||||
|
os.path.isfile(os.path.join(fn,'__init__.py'))
|
||||||
|
):
|
||||||
|
out.append(prefix+name); stack.append((fn,prefix+name+'.'))
|
||||||
|
for pat in list(exclude)+['ez_setup', 'distribute_setup']:
|
||||||
|
from fnmatch import fnmatchcase
|
||||||
|
out = [item for item in out if not fnmatchcase(item,pat)]
|
||||||
|
return out
|
||||||
|
|
||||||
|
setup = distutils.core.setup
|
||||||
|
|
||||||
|
_Command = _get_unpatched(_Command)
|
||||||
|
|
||||||
|
class Command(_Command):
|
||||||
|
__doc__ = _Command.__doc__
|
||||||
|
|
||||||
|
command_consumes_arguments = False
|
||||||
|
|
||||||
|
def __init__(self, dist, **kw):
|
||||||
|
# Add support for keyword arguments
|
||||||
|
_Command.__init__(self,dist)
|
||||||
|
for k,v in kw.items():
|
||||||
|
setattr(self,k,v)
|
||||||
|
|
||||||
|
def reinitialize_command(self, command, reinit_subcommands=0, **kw):
|
||||||
|
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
|
||||||
|
for k,v in kw.items():
|
||||||
|
setattr(cmd,k,v) # update command with keywords
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
import distutils.core
|
||||||
|
distutils.core.Command = Command # we can't patch distutils.cmd, alas
|
||||||
|
|
||||||
|
def findall(dir = os.curdir):
|
||||||
|
"""Find all files under 'dir' and return the list of full filenames
|
||||||
|
(relative to 'dir').
|
||||||
|
"""
|
||||||
|
all_files = []
|
||||||
|
for base, dirs, files in os.walk(dir):
|
||||||
|
if base==os.curdir or base.startswith(os.curdir+os.sep):
|
||||||
|
base = base[2:]
|
||||||
|
if base:
|
||||||
|
files = [os.path.join(base, f) for f in files]
|
||||||
|
all_files.extend(filter(os.path.isfile, files))
|
||||||
|
return all_files
|
||||||
|
|
||||||
|
import distutils.filelist
|
||||||
|
distutils.filelist.findall = findall # fix findall bug in distutils.
|
||||||
|
|
||||||
|
# sys.dont_write_bytecode was introduced in Python 2.6.
|
||||||
|
if ((hasattr(sys, "dont_write_bytecode") and sys.dont_write_bytecode) or
|
||||||
|
(not hasattr(sys, "dont_write_bytecode") and os.environ.get("PYTHONDONTWRITEBYTECODE"))):
|
||||||
|
_dont_write_bytecode = True
|
||||||
|
else:
|
||||||
|
_dont_write_bytecode = False
|
||||||
+214
@@ -0,0 +1,214 @@
|
|||||||
|
"""Utilities for extracting common archive formats"""
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter",
|
||||||
|
"UnrecognizedFormat", "extraction_drivers", "unpack_directory",
|
||||||
|
]
|
||||||
|
|
||||||
|
import zipfile, tarfile, os, shutil
|
||||||
|
from pkg_resources import ensure_directory
|
||||||
|
from distutils.errors import DistutilsError
|
||||||
|
|
||||||
|
class UnrecognizedFormat(DistutilsError):
|
||||||
|
"""Couldn't recognize the archive type"""
|
||||||
|
|
||||||
|
def default_filter(src,dst):
|
||||||
|
"""The default progress/filter callback; returns True for all files"""
|
||||||
|
return dst
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_archive(filename, extract_dir, progress_filter=default_filter,
|
||||||
|
drivers=None
|
||||||
|
):
|
||||||
|
"""Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
|
||||||
|
|
||||||
|
`progress_filter` is a function taking two arguments: a source path
|
||||||
|
internal to the archive ('/'-separated), and a filesystem path where it
|
||||||
|
will be extracted. The callback must return the desired extract path
|
||||||
|
(which may be the same as the one passed in), or else ``None`` to skip
|
||||||
|
that file or directory. The callback can thus be used to report on the
|
||||||
|
progress of the extraction, as well as to filter the items extracted or
|
||||||
|
alter their extraction paths.
|
||||||
|
|
||||||
|
`drivers`, if supplied, must be a non-empty sequence of functions with the
|
||||||
|
same signature as this function (minus the `drivers` argument), that raise
|
||||||
|
``UnrecognizedFormat`` if they do not support extracting the designated
|
||||||
|
archive type. The `drivers` are tried in sequence until one is found that
|
||||||
|
does not raise an error, or until all are exhausted (in which case
|
||||||
|
``UnrecognizedFormat`` is raised). If you do not supply a sequence of
|
||||||
|
drivers, the module's ``extraction_drivers`` constant will be used, which
|
||||||
|
means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that
|
||||||
|
order.
|
||||||
|
"""
|
||||||
|
for driver in drivers or extraction_drivers:
|
||||||
|
try:
|
||||||
|
driver(filename, extract_dir, progress_filter)
|
||||||
|
except UnrecognizedFormat:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise UnrecognizedFormat(
|
||||||
|
"Not a recognized archive type: %s" % filename
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_directory(filename, extract_dir, progress_filter=default_filter):
|
||||||
|
""""Unpack" a directory, using the same interface as for archives
|
||||||
|
|
||||||
|
Raises ``UnrecognizedFormat`` if `filename` is not a directory
|
||||||
|
"""
|
||||||
|
if not os.path.isdir(filename):
|
||||||
|
raise UnrecognizedFormat("%s is not a directory" % (filename,))
|
||||||
|
|
||||||
|
paths = {filename:('',extract_dir)}
|
||||||
|
for base, dirs, files in os.walk(filename):
|
||||||
|
src,dst = paths[base]
|
||||||
|
for d in dirs:
|
||||||
|
paths[os.path.join(base,d)] = src+d+'/', os.path.join(dst,d)
|
||||||
|
for f in files:
|
||||||
|
name = src+f
|
||||||
|
target = os.path.join(dst,f)
|
||||||
|
target = progress_filter(src+f, target)
|
||||||
|
if not target:
|
||||||
|
continue # skip non-files
|
||||||
|
ensure_directory(target)
|
||||||
|
f = os.path.join(base,f)
|
||||||
|
shutil.copyfile(f, target)
|
||||||
|
shutil.copystat(f, target)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
|
||||||
|
"""Unpack zip `filename` to `extract_dir`
|
||||||
|
|
||||||
|
Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined
|
||||||
|
by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation
|
||||||
|
of the `progress_filter` argument.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not zipfile.is_zipfile(filename):
|
||||||
|
raise UnrecognizedFormat("%s is not a zip file" % (filename,))
|
||||||
|
|
||||||
|
z = zipfile.ZipFile(filename)
|
||||||
|
try:
|
||||||
|
for info in z.infolist():
|
||||||
|
name = info.filename
|
||||||
|
|
||||||
|
# don't extract absolute paths or ones with .. in them
|
||||||
|
if name.startswith('/') or '..' in name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
target = os.path.join(extract_dir, *name.split('/'))
|
||||||
|
target = progress_filter(name, target)
|
||||||
|
if not target:
|
||||||
|
continue
|
||||||
|
if name.endswith('/'):
|
||||||
|
# directory
|
||||||
|
ensure_directory(target)
|
||||||
|
else:
|
||||||
|
# file
|
||||||
|
ensure_directory(target)
|
||||||
|
data = z.read(info.filename)
|
||||||
|
f = open(target,'wb')
|
||||||
|
try:
|
||||||
|
f.write(data)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
del data
|
||||||
|
unix_attributes = info.external_attr >> 16
|
||||||
|
if unix_attributes:
|
||||||
|
os.chmod(target, unix_attributes)
|
||||||
|
finally:
|
||||||
|
z.close()
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):
|
||||||
|
"""Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
|
||||||
|
|
||||||
|
Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined
|
||||||
|
by ``tarfile.open()``). See ``unpack_archive()`` for an explanation
|
||||||
|
of the `progress_filter` argument.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
tarobj = tarfile.open(filename)
|
||||||
|
except tarfile.TarError:
|
||||||
|
raise UnrecognizedFormat(
|
||||||
|
"%s is not a compressed or uncompressed tar file" % (filename,)
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
tarobj.chown = lambda *args: None # don't do any chowning!
|
||||||
|
for member in tarobj:
|
||||||
|
name = member.name
|
||||||
|
# don't extract absolute paths or ones with .. in them
|
||||||
|
if not name.startswith('/') and '..' not in name:
|
||||||
|
prelim_dst = os.path.join(extract_dir, *name.split('/'))
|
||||||
|
final_dst = progress_filter(name, prelim_dst)
|
||||||
|
# If progress_filter returns None, then we do not extract
|
||||||
|
# this file
|
||||||
|
# TODO: Do we really need to limit to just these file types?
|
||||||
|
# tarobj.extract() will handle all files on all platforms,
|
||||||
|
# turning file types that aren't allowed on that platform into
|
||||||
|
# regular files.
|
||||||
|
if final_dst and (member.isfile() or member.isdir() or
|
||||||
|
member.islnk() or member.issym()):
|
||||||
|
tarobj.extract(member, extract_dir)
|
||||||
|
if final_dst != prelim_dst:
|
||||||
|
shutil.move(prelim_dst, final_dst)
|
||||||
|
return True
|
||||||
|
finally:
|
||||||
|
tarobj.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,21 @@
|
|||||||
|
__all__ = [
|
||||||
|
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
|
||||||
|
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
|
||||||
|
'sdist', 'setopt', 'test', 'upload', 'install_egg_info', 'install_scripts',
|
||||||
|
'register', 'bdist_wininst', 'upload_docs',
|
||||||
|
]
|
||||||
|
|
||||||
|
from setuptools.command import install_scripts
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.version>='2.5':
|
||||||
|
# In Python 2.5 and above, distutils includes its own upload command
|
||||||
|
__all__.remove('upload')
|
||||||
|
|
||||||
|
from distutils.command.bdist import bdist
|
||||||
|
|
||||||
|
if 'egg' not in bdist.format_commands:
|
||||||
|
bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
|
||||||
|
bdist.format_commands.append('egg')
|
||||||
|
|
||||||
|
del bdist, sys
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import distutils, os
|
||||||
|
from setuptools import Command
|
||||||
|
from distutils.util import convert_path
|
||||||
|
from distutils import log
|
||||||
|
from distutils.errors import *
|
||||||
|
from setuptools.command.setopt import edit_config, option_base, config_file
|
||||||
|
|
||||||
|
def shquote(arg):
|
||||||
|
"""Quote an argument for later parsing by shlex.split()"""
|
||||||
|
for c in '"', "'", "\\", "#":
|
||||||
|
if c in arg: return repr(arg)
|
||||||
|
if arg.split()<>[arg]:
|
||||||
|
return repr(arg)
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
class alias(option_base):
|
||||||
|
"""Define a shortcut that invokes one or more commands"""
|
||||||
|
|
||||||
|
description = "define a shortcut to invoke one or more commands"
|
||||||
|
command_consumes_arguments = True
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('remove', 'r', 'remove (unset) the alias'),
|
||||||
|
] + option_base.user_options
|
||||||
|
|
||||||
|
boolean_options = option_base.boolean_options + ['remove']
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
option_base.initialize_options(self)
|
||||||
|
self.args = None
|
||||||
|
self.remove = None
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
option_base.finalize_options(self)
|
||||||
|
if self.remove and len(self.args)<>1:
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"Must specify exactly one argument (the alias name) when "
|
||||||
|
"using --remove"
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
aliases = self.distribution.get_option_dict('aliases')
|
||||||
|
|
||||||
|
if not self.args:
|
||||||
|
print "Command Aliases"
|
||||||
|
print "---------------"
|
||||||
|
for alias in aliases:
|
||||||
|
print "setup.py alias", format_alias(alias, aliases)
|
||||||
|
return
|
||||||
|
|
||||||
|
elif len(self.args)==1:
|
||||||
|
alias, = self.args
|
||||||
|
if self.remove:
|
||||||
|
command = None
|
||||||
|
elif alias in aliases:
|
||||||
|
print "setup.py alias", format_alias(alias, aliases)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print "No alias definition found for %r" % alias
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
alias = self.args[0]
|
||||||
|
command = ' '.join(map(shquote,self.args[1:]))
|
||||||
|
|
||||||
|
edit_config(self.filename, {'aliases': {alias:command}}, self.dry_run)
|
||||||
|
|
||||||
|
|
||||||
|
def format_alias(name, aliases):
|
||||||
|
source, command = aliases[name]
|
||||||
|
if source == config_file('global'):
|
||||||
|
source = '--global-config '
|
||||||
|
elif source == config_file('user'):
|
||||||
|
source = '--user-config '
|
||||||
|
elif source == config_file('local'):
|
||||||
|
source = ''
|
||||||
|
else:
|
||||||
|
source = '--filename=%r' % source
|
||||||
|
return source+name+' '+command
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,548 @@
|
|||||||
|
"""setuptools.command.bdist_egg
|
||||||
|
|
||||||
|
Build .egg distributions"""
|
||||||
|
|
||||||
|
# This module should be kept compatible with Python 2.3
|
||||||
|
import sys, os, marshal
|
||||||
|
from setuptools import Command
|
||||||
|
from distutils.dir_util import remove_tree, mkpath
|
||||||
|
try:
|
||||||
|
from distutils.sysconfig import get_python_version, get_python_lib
|
||||||
|
except ImportError:
|
||||||
|
from sysconfig import get_python_version
|
||||||
|
from distutils.sysconfig import get_python_lib
|
||||||
|
|
||||||
|
from distutils import log
|
||||||
|
from distutils.errors import DistutilsSetupError
|
||||||
|
from pkg_resources import get_build_platform, Distribution, ensure_directory
|
||||||
|
from pkg_resources import EntryPoint
|
||||||
|
from types import CodeType
|
||||||
|
from setuptools.extension import Library
|
||||||
|
|
||||||
|
def strip_module(filename):
|
||||||
|
if '.' in filename:
|
||||||
|
filename = os.path.splitext(filename)[0]
|
||||||
|
if filename.endswith('module'):
|
||||||
|
filename = filename[:-6]
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def write_stub(resource, pyfile):
|
||||||
|
f = open(pyfile,'w')
|
||||||
|
f.write('\n'.join([
|
||||||
|
"def __bootstrap__():",
|
||||||
|
" global __bootstrap__, __loader__, __file__",
|
||||||
|
" import sys, pkg_resources, imp",
|
||||||
|
" __file__ = pkg_resources.resource_filename(__name__,%r)"
|
||||||
|
% resource,
|
||||||
|
" __loader__ = None; del __bootstrap__, __loader__",
|
||||||
|
" imp.load_dynamic(__name__,__file__)",
|
||||||
|
"__bootstrap__()",
|
||||||
|
"" # terminal \n
|
||||||
|
]))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# stub __init__.py for packages distributed without one
|
||||||
|
NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)'
|
||||||
|
|
||||||
|
class bdist_egg(Command):
|
||||||
|
|
||||||
|
description = "create an \"egg\" distribution"
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('bdist-dir=', 'b',
|
||||||
|
"temporary directory for creating the distribution"),
|
||||||
|
('plat-name=', 'p',
|
||||||
|
"platform name to embed in generated filenames "
|
||||||
|
"(default: %s)" % get_build_platform()),
|
||||||
|
('exclude-source-files', None,
|
||||||
|
"remove all .py files from the generated egg"),
|
||||||
|
('keep-temp', 'k',
|
||||||
|
"keep the pseudo-installation tree around after " +
|
||||||
|
"creating the distribution archive"),
|
||||||
|
('dist-dir=', 'd',
|
||||||
|
"directory to put final built distributions in"),
|
||||||
|
('skip-build', None,
|
||||||
|
"skip rebuilding everything (for testing/debugging)"),
|
||||||
|
]
|
||||||
|
|
||||||
|
boolean_options = [
|
||||||
|
'keep-temp', 'skip-build', 'exclude-source-files'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_options (self):
|
||||||
|
self.bdist_dir = None
|
||||||
|
self.plat_name = None
|
||||||
|
self.keep_temp = 0
|
||||||
|
self.dist_dir = None
|
||||||
|
self.skip_build = 0
|
||||||
|
self.egg_output = None
|
||||||
|
self.exclude_source_files = None
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
|
||||||
|
self.egg_info = ei_cmd.egg_info
|
||||||
|
|
||||||
|
if self.bdist_dir is None:
|
||||||
|
bdist_base = self.get_finalized_command('bdist').bdist_base
|
||||||
|
self.bdist_dir = os.path.join(bdist_base, 'egg')
|
||||||
|
|
||||||
|
if self.plat_name is None:
|
||||||
|
self.plat_name = get_build_platform()
|
||||||
|
|
||||||
|
self.set_undefined_options('bdist',('dist_dir', 'dist_dir'))
|
||||||
|
|
||||||
|
if self.egg_output is None:
|
||||||
|
|
||||||
|
# Compute filename of the output egg
|
||||||
|
basename = Distribution(
|
||||||
|
None, None, ei_cmd.egg_name, ei_cmd.egg_version,
|
||||||
|
get_python_version(),
|
||||||
|
self.distribution.has_ext_modules() and self.plat_name
|
||||||
|
).egg_name()
|
||||||
|
|
||||||
|
self.egg_output = os.path.join(self.dist_dir, basename+'.egg')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def do_install_data(self):
|
||||||
|
# Hack for packages that install data to install's --install-lib
|
||||||
|
self.get_finalized_command('install').install_lib = self.bdist_dir
|
||||||
|
|
||||||
|
site_packages = os.path.normcase(os.path.realpath(get_python_lib()))
|
||||||
|
old, self.distribution.data_files = self.distribution.data_files,[]
|
||||||
|
|
||||||
|
for item in old:
|
||||||
|
if isinstance(item,tuple) and len(item)==2:
|
||||||
|
if os.path.isabs(item[0]):
|
||||||
|
realpath = os.path.realpath(item[0])
|
||||||
|
normalized = os.path.normcase(realpath)
|
||||||
|
if normalized==site_packages or normalized.startswith(
|
||||||
|
site_packages+os.sep
|
||||||
|
):
|
||||||
|
item = realpath[len(site_packages)+1:], item[1]
|
||||||
|
# XXX else: raise ???
|
||||||
|
self.distribution.data_files.append(item)
|
||||||
|
|
||||||
|
try:
|
||||||
|
log.info("installing package data to %s" % self.bdist_dir)
|
||||||
|
self.call_command('install_data', force=0, root=None)
|
||||||
|
finally:
|
||||||
|
self.distribution.data_files = old
|
||||||
|
|
||||||
|
|
||||||
|
def get_outputs(self):
|
||||||
|
return [self.egg_output]
|
||||||
|
|
||||||
|
|
||||||
|
def call_command(self,cmdname,**kw):
|
||||||
|
"""Invoke reinitialized command `cmdname` with keyword args"""
|
||||||
|
for dirname in INSTALL_DIRECTORY_ATTRS:
|
||||||
|
kw.setdefault(dirname,self.bdist_dir)
|
||||||
|
kw.setdefault('skip_build',self.skip_build)
|
||||||
|
kw.setdefault('dry_run', self.dry_run)
|
||||||
|
cmd = self.reinitialize_command(cmdname, **kw)
|
||||||
|
self.run_command(cmdname)
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# Generate metadata first
|
||||||
|
self.run_command("egg_info")
|
||||||
|
|
||||||
|
# We run install_lib before install_data, because some data hacks
|
||||||
|
# pull their data path from the install_lib command.
|
||||||
|
log.info("installing library code to %s" % self.bdist_dir)
|
||||||
|
instcmd = self.get_finalized_command('install')
|
||||||
|
old_root = instcmd.root; instcmd.root = None
|
||||||
|
cmd = self.call_command('install_lib', warn_dir=0)
|
||||||
|
instcmd.root = old_root
|
||||||
|
|
||||||
|
all_outputs, ext_outputs = self.get_ext_outputs()
|
||||||
|
self.stubs = []
|
||||||
|
to_compile = []
|
||||||
|
for (p,ext_name) in enumerate(ext_outputs):
|
||||||
|
filename,ext = os.path.splitext(ext_name)
|
||||||
|
pyfile = os.path.join(self.bdist_dir, strip_module(filename)+'.py')
|
||||||
|
self.stubs.append(pyfile)
|
||||||
|
log.info("creating stub loader for %s" % ext_name)
|
||||||
|
if not self.dry_run:
|
||||||
|
write_stub(os.path.basename(ext_name), pyfile)
|
||||||
|
to_compile.append(pyfile)
|
||||||
|
ext_outputs[p] = ext_name.replace(os.sep,'/')
|
||||||
|
|
||||||
|
to_compile.extend(self.make_init_files())
|
||||||
|
if to_compile:
|
||||||
|
cmd.byte_compile(to_compile)
|
||||||
|
|
||||||
|
if self.distribution.data_files:
|
||||||
|
self.do_install_data()
|
||||||
|
|
||||||
|
# Make the EGG-INFO directory
|
||||||
|
archive_root = self.bdist_dir
|
||||||
|
egg_info = os.path.join(archive_root,'EGG-INFO')
|
||||||
|
self.mkpath(egg_info)
|
||||||
|
if self.distribution.scripts:
|
||||||
|
script_dir = os.path.join(egg_info, 'scripts')
|
||||||
|
log.info("installing scripts to %s" % script_dir)
|
||||||
|
self.call_command('install_scripts',install_dir=script_dir,no_ep=1)
|
||||||
|
|
||||||
|
self.copy_metadata_to(egg_info)
|
||||||
|
native_libs = os.path.join(egg_info, "native_libs.txt")
|
||||||
|
if all_outputs:
|
||||||
|
log.info("writing %s" % native_libs)
|
||||||
|
if not self.dry_run:
|
||||||
|
ensure_directory(native_libs)
|
||||||
|
libs_file = open(native_libs, 'wt')
|
||||||
|
libs_file.write('\n'.join(all_outputs))
|
||||||
|
libs_file.write('\n')
|
||||||
|
libs_file.close()
|
||||||
|
elif os.path.isfile(native_libs):
|
||||||
|
log.info("removing %s" % native_libs)
|
||||||
|
if not self.dry_run:
|
||||||
|
os.unlink(native_libs)
|
||||||
|
|
||||||
|
write_safety_flag(
|
||||||
|
os.path.join(archive_root,'EGG-INFO'), self.zip_safe()
|
||||||
|
)
|
||||||
|
|
||||||
|
if os.path.exists(os.path.join(self.egg_info,'depends.txt')):
|
||||||
|
log.warn(
|
||||||
|
"WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
|
||||||
|
"Use the install_requires/extras_require setup() args instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.exclude_source_files:
|
||||||
|
self.zap_pyfiles()
|
||||||
|
|
||||||
|
# Make the archive
|
||||||
|
make_zipfile(self.egg_output, archive_root, verbose=self.verbose,
|
||||||
|
dry_run=self.dry_run, mode=self.gen_header())
|
||||||
|
if not self.keep_temp:
|
||||||
|
remove_tree(self.bdist_dir, dry_run=self.dry_run)
|
||||||
|
|
||||||
|
# Add to 'Distribution.dist_files' so that the "upload" command works
|
||||||
|
getattr(self.distribution,'dist_files',[]).append(
|
||||||
|
('bdist_egg',get_python_version(),self.egg_output))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def zap_pyfiles(self):
|
||||||
|
log.info("Removing .py files from temporary directory")
|
||||||
|
for base,dirs,files in walk_egg(self.bdist_dir):
|
||||||
|
for name in files:
|
||||||
|
if name.endswith('.py'):
|
||||||
|
path = os.path.join(base,name)
|
||||||
|
log.debug("Deleting %s", path)
|
||||||
|
os.unlink(path)
|
||||||
|
|
||||||
|
def zip_safe(self):
|
||||||
|
safe = getattr(self.distribution,'zip_safe',None)
|
||||||
|
if safe is not None:
|
||||||
|
return safe
|
||||||
|
log.warn("zip_safe flag not set; analyzing archive contents...")
|
||||||
|
return analyze_egg(self.bdist_dir, self.stubs)
|
||||||
|
|
||||||
|
def make_init_files(self):
|
||||||
|
"""Create missing package __init__ files"""
|
||||||
|
init_files = []
|
||||||
|
for base,dirs,files in walk_egg(self.bdist_dir):
|
||||||
|
if base==self.bdist_dir:
|
||||||
|
# don't put an __init__ in the root
|
||||||
|
continue
|
||||||
|
for name in files:
|
||||||
|
if name.endswith('.py'):
|
||||||
|
if '__init__.py' not in files:
|
||||||
|
pkg = base[len(self.bdist_dir)+1:].replace(os.sep,'.')
|
||||||
|
if self.distribution.has_contents_for(pkg):
|
||||||
|
log.warn("Creating missing __init__.py for %s",pkg)
|
||||||
|
filename = os.path.join(base,'__init__.py')
|
||||||
|
if not self.dry_run:
|
||||||
|
f = open(filename,'w'); f.write(NS_PKG_STUB)
|
||||||
|
f.close()
|
||||||
|
init_files.append(filename)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# not a package, don't traverse to subdirectories
|
||||||
|
dirs[:] = []
|
||||||
|
|
||||||
|
return init_files
|
||||||
|
|
||||||
|
def gen_header(self):
|
||||||
|
epm = EntryPoint.parse_map(self.distribution.entry_points or '')
|
||||||
|
ep = epm.get('setuptools.installation',{}).get('eggsecutable')
|
||||||
|
if ep is None:
|
||||||
|
return 'w' # not an eggsecutable, do it the usual way.
|
||||||
|
|
||||||
|
if not ep.attrs or ep.extras:
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"eggsecutable entry point (%r) cannot have 'extras' "
|
||||||
|
"or refer to a module" % (ep,)
|
||||||
|
)
|
||||||
|
|
||||||
|
pyver = sys.version[:3]
|
||||||
|
pkg = ep.module_name
|
||||||
|
full = '.'.join(ep.attrs)
|
||||||
|
base = ep.attrs[0]
|
||||||
|
basename = os.path.basename(self.egg_output)
|
||||||
|
|
||||||
|
header = (
|
||||||
|
"#!/bin/sh\n"
|
||||||
|
'if [ `basename $0` = "%(basename)s" ]\n'
|
||||||
|
'then exec python%(pyver)s -c "'
|
||||||
|
"import sys, os; sys.path.insert(0, os.path.abspath('$0')); "
|
||||||
|
"from %(pkg)s import %(base)s; sys.exit(%(full)s())"
|
||||||
|
'" "$@"\n'
|
||||||
|
'else\n'
|
||||||
|
' echo $0 is not the correct name for this egg file.\n'
|
||||||
|
' echo Please rename it back to %(basename)s and try again.\n'
|
||||||
|
' exec false\n'
|
||||||
|
'fi\n'
|
||||||
|
|
||||||
|
) % locals()
|
||||||
|
|
||||||
|
if not self.dry_run:
|
||||||
|
mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run)
|
||||||
|
f = open(self.egg_output, 'w')
|
||||||
|
f.write(header)
|
||||||
|
f.close()
|
||||||
|
return 'a'
|
||||||
|
|
||||||
|
|
||||||
|
def copy_metadata_to(self, target_dir):
|
||||||
|
"Copy metadata (egg info) to the target_dir"
|
||||||
|
# normalize the path (so that a forward-slash in egg_info will
|
||||||
|
# match using startswith below)
|
||||||
|
norm_egg_info = os.path.normpath(self.egg_info)
|
||||||
|
prefix = os.path.join(norm_egg_info,'')
|
||||||
|
for path in self.ei_cmd.filelist.files:
|
||||||
|
if path.startswith(prefix):
|
||||||
|
target = os.path.join(target_dir, path[len(prefix):])
|
||||||
|
ensure_directory(target)
|
||||||
|
self.copy_file(path, target)
|
||||||
|
|
||||||
|
def get_ext_outputs(self):
|
||||||
|
"""Get a list of relative paths to C extensions in the output distro"""
|
||||||
|
|
||||||
|
all_outputs = []
|
||||||
|
ext_outputs = []
|
||||||
|
|
||||||
|
paths = {self.bdist_dir:''}
|
||||||
|
for base, dirs, files in os.walk(self.bdist_dir):
|
||||||
|
for filename in files:
|
||||||
|
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
|
||||||
|
all_outputs.append(paths[base]+filename)
|
||||||
|
for filename in dirs:
|
||||||
|
paths[os.path.join(base,filename)] = paths[base]+filename+'/'
|
||||||
|
|
||||||
|
if self.distribution.has_ext_modules():
|
||||||
|
build_cmd = self.get_finalized_command('build_ext')
|
||||||
|
for ext in build_cmd.extensions:
|
||||||
|
if isinstance(ext,Library):
|
||||||
|
continue
|
||||||
|
fullname = build_cmd.get_ext_fullname(ext.name)
|
||||||
|
filename = build_cmd.get_ext_filename(fullname)
|
||||||
|
if not os.path.basename(filename).startswith('dl-'):
|
||||||
|
if os.path.exists(os.path.join(self.bdist_dir,filename)):
|
||||||
|
ext_outputs.append(filename)
|
||||||
|
|
||||||
|
return all_outputs, ext_outputs
|
||||||
|
|
||||||
|
|
||||||
|
NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def walk_egg(egg_dir):
|
||||||
|
"""Walk an unpacked egg's contents, skipping the metadata directory"""
|
||||||
|
walker = os.walk(egg_dir)
|
||||||
|
base,dirs,files = walker.next()
|
||||||
|
if 'EGG-INFO' in dirs:
|
||||||
|
dirs.remove('EGG-INFO')
|
||||||
|
yield base,dirs,files
|
||||||
|
for bdf in walker:
|
||||||
|
yield bdf
|
||||||
|
|
||||||
|
def analyze_egg(egg_dir, stubs):
|
||||||
|
# check for existing flag in EGG-INFO
|
||||||
|
for flag,fn in safety_flags.items():
|
||||||
|
if os.path.exists(os.path.join(egg_dir,'EGG-INFO',fn)):
|
||||||
|
return flag
|
||||||
|
if not can_scan(): return False
|
||||||
|
safe = True
|
||||||
|
for base, dirs, files in walk_egg(egg_dir):
|
||||||
|
for name in files:
|
||||||
|
if name.endswith('.py') or name.endswith('.pyw'):
|
||||||
|
continue
|
||||||
|
elif name.endswith('.pyc') or name.endswith('.pyo'):
|
||||||
|
# always scan, even if we already know we're not safe
|
||||||
|
safe = scan_module(egg_dir, base, name, stubs) and safe
|
||||||
|
return safe
|
||||||
|
|
||||||
|
def write_safety_flag(egg_dir, safe):
|
||||||
|
# Write or remove zip safety flag file(s)
|
||||||
|
for flag,fn in safety_flags.items():
|
||||||
|
fn = os.path.join(egg_dir, fn)
|
||||||
|
if os.path.exists(fn):
|
||||||
|
if safe is None or bool(safe)<>flag:
|
||||||
|
os.unlink(fn)
|
||||||
|
elif safe is not None and bool(safe)==flag:
|
||||||
|
f=open(fn,'wt'); f.write('\n'); f.close()
|
||||||
|
|
||||||
|
safety_flags = {
|
||||||
|
True: 'zip-safe',
|
||||||
|
False: 'not-zip-safe',
|
||||||
|
}
|
||||||
|
|
||||||
|
def scan_module(egg_dir, base, name, stubs):
|
||||||
|
"""Check whether module possibly uses unsafe-for-zipfile stuff"""
|
||||||
|
|
||||||
|
filename = os.path.join(base,name)
|
||||||
|
if filename[:-1] in stubs:
|
||||||
|
return True # Extension module
|
||||||
|
pkg = base[len(egg_dir)+1:].replace(os.sep,'.')
|
||||||
|
module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0]
|
||||||
|
if sys.version_info < (3, 3):
|
||||||
|
skip = 8 # skip magic & date
|
||||||
|
else:
|
||||||
|
skip = 12 # skip magic & date & file size
|
||||||
|
f = open(filename,'rb'); f.read(skip)
|
||||||
|
code = marshal.load(f); f.close()
|
||||||
|
safe = True
|
||||||
|
symbols = dict.fromkeys(iter_symbols(code))
|
||||||
|
for bad in ['__file__', '__path__']:
|
||||||
|
if bad in symbols:
|
||||||
|
log.warn("%s: module references %s", module, bad)
|
||||||
|
safe = False
|
||||||
|
if 'inspect' in symbols:
|
||||||
|
for bad in [
|
||||||
|
'getsource', 'getabsfile', 'getsourcefile', 'getfile'
|
||||||
|
'getsourcelines', 'findsource', 'getcomments', 'getframeinfo',
|
||||||
|
'getinnerframes', 'getouterframes', 'stack', 'trace'
|
||||||
|
]:
|
||||||
|
if bad in symbols:
|
||||||
|
log.warn("%s: module MAY be using inspect.%s", module, bad)
|
||||||
|
safe = False
|
||||||
|
if '__name__' in symbols and '__main__' in symbols and '.' not in module:
|
||||||
|
if sys.version[:3]=="2.4": # -m works w/zipfiles in 2.5
|
||||||
|
log.warn("%s: top-level module may be 'python -m' script", module)
|
||||||
|
safe = False
|
||||||
|
return safe
|
||||||
|
|
||||||
|
def iter_symbols(code):
|
||||||
|
"""Yield names and strings used by `code` and its nested code objects"""
|
||||||
|
for name in code.co_names: yield name
|
||||||
|
for const in code.co_consts:
|
||||||
|
if isinstance(const,basestring):
|
||||||
|
yield const
|
||||||
|
elif isinstance(const,CodeType):
|
||||||
|
for name in iter_symbols(const):
|
||||||
|
yield name
|
||||||
|
|
||||||
|
def can_scan():
|
||||||
|
if not sys.platform.startswith('java') and sys.platform != 'cli':
|
||||||
|
# CPython, PyPy, etc.
|
||||||
|
return True
|
||||||
|
log.warn("Unable to analyze compiled code on this platform.")
|
||||||
|
log.warn("Please ask the author to include a 'zip_safe'"
|
||||||
|
" setting (either True or False) in the package's setup.py")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Attribute names of options for commands that might need to be convinced to
|
||||||
|
# install to the egg build directory
|
||||||
|
|
||||||
|
INSTALL_DIRECTORY_ATTRS = [
|
||||||
|
'install_lib', 'install_dir', 'install_data', 'install_base'
|
||||||
|
]
|
||||||
|
|
||||||
|
def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=None,
|
||||||
|
mode='w'
|
||||||
|
):
|
||||||
|
"""Create a zip file from all the files under 'base_dir'. The output
|
||||||
|
zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
|
||||||
|
Python module (if available) or the InfoZIP "zip" utility (if installed
|
||||||
|
and found on the default search path). If neither tool is available,
|
||||||
|
raises DistutilsExecError. Returns the name of the output zip file.
|
||||||
|
"""
|
||||||
|
import zipfile
|
||||||
|
mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
|
||||||
|
log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
|
||||||
|
|
||||||
|
def visit(z, dirname, names):
|
||||||
|
for name in names:
|
||||||
|
path = os.path.normpath(os.path.join(dirname, name))
|
||||||
|
if os.path.isfile(path):
|
||||||
|
p = path[len(base_dir)+1:]
|
||||||
|
if not dry_run:
|
||||||
|
z.write(path, p)
|
||||||
|
log.debug("adding '%s'" % p)
|
||||||
|
|
||||||
|
if compress is None:
|
||||||
|
compress = (sys.version>="2.4") # avoid 2.3 zipimport bug when 64 bits
|
||||||
|
|
||||||
|
compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)]
|
||||||
|
if not dry_run:
|
||||||
|
z = zipfile.ZipFile(zip_filename, mode, compression=compression)
|
||||||
|
for dirname, dirs, files in os.walk(base_dir):
|
||||||
|
visit(z, dirname, files)
|
||||||
|
z.close()
|
||||||
|
else:
|
||||||
|
for dirname, dirs, files in os.walk(base_dir):
|
||||||
|
visit(None, dirname, files)
|
||||||
|
return zip_filename
|
||||||
|
#
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# This is just a kludge so that bdist_rpm doesn't guess wrong about the
|
||||||
|
# distribution name and version, if the egg_info command is going to alter
|
||||||
|
# them, another kludge to allow you to build old-style non-egg RPMs, and
|
||||||
|
# finally, a kludge to track .rpm files for uploading when run on Python <2.5.
|
||||||
|
|
||||||
|
from distutils.command.bdist_rpm import bdist_rpm as _bdist_rpm
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
class bdist_rpm(_bdist_rpm):
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
_bdist_rpm.initialize_options(self)
|
||||||
|
self.no_egg = None
|
||||||
|
|
||||||
|
if sys.version<"2.5":
|
||||||
|
# Track for uploading any .rpm file(s) moved to self.dist_dir
|
||||||
|
def move_file(self, src, dst, level=1):
|
||||||
|
_bdist_rpm.move_file(self, src, dst, level)
|
||||||
|
if dst==self.dist_dir and src.endswith('.rpm'):
|
||||||
|
getattr(self.distribution,'dist_files',[]).append(
|
||||||
|
('bdist_rpm',
|
||||||
|
src.endswith('.src.rpm') and 'any' or sys.version[:3],
|
||||||
|
os.path.join(dst, os.path.basename(src)))
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.run_command('egg_info') # ensure distro name is up-to-date
|
||||||
|
_bdist_rpm.run(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _make_spec_file(self):
|
||||||
|
version = self.distribution.get_version()
|
||||||
|
rpmversion = version.replace('-','_')
|
||||||
|
spec = _bdist_rpm._make_spec_file(self)
|
||||||
|
line23 = '%define version '+version
|
||||||
|
line24 = '%define version '+rpmversion
|
||||||
|
spec = [
|
||||||
|
line.replace(
|
||||||
|
"Source0: %{name}-%{version}.tar",
|
||||||
|
"Source0: %{name}-%{unmangled_version}.tar"
|
||||||
|
).replace(
|
||||||
|
"setup.py install ",
|
||||||
|
"setup.py install --single-version-externally-managed "
|
||||||
|
).replace(
|
||||||
|
"%setup",
|
||||||
|
"%setup -n %{name}-%{unmangled_version}"
|
||||||
|
).replace(line23,line24)
|
||||||
|
for line in spec
|
||||||
|
]
|
||||||
|
spec.insert(spec.index(line24)+1, "%define unmangled_version "+version)
|
||||||
|
return spec
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
from distutils.command.bdist_wininst import bdist_wininst as _bdist_wininst
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
class bdist_wininst(_bdist_wininst):
|
||||||
|
|
||||||
|
def create_exe(self, arcname, fullname, bitmap=None):
|
||||||
|
_bdist_wininst.create_exe(self, arcname, fullname, bitmap)
|
||||||
|
dist_files = getattr(self.distribution, 'dist_files', [])
|
||||||
|
|
||||||
|
if self.target_version:
|
||||||
|
installer_name = os.path.join(self.dist_dir,
|
||||||
|
"%s.win32-py%s.exe" %
|
||||||
|
(fullname, self.target_version))
|
||||||
|
pyversion = self.target_version
|
||||||
|
|
||||||
|
# fix 2.5 bdist_wininst ignoring --target-version spec
|
||||||
|
bad = ('bdist_wininst','any',installer_name)
|
||||||
|
if bad in dist_files:
|
||||||
|
dist_files.remove(bad)
|
||||||
|
else:
|
||||||
|
installer_name = os.path.join(self.dist_dir,
|
||||||
|
"%s.win32.exe" % fullname)
|
||||||
|
pyversion = 'any'
|
||||||
|
good = ('bdist_wininst', pyversion, installer_name)
|
||||||
|
if good not in dist_files:
|
||||||
|
dist_files.append(good)
|
||||||
|
|
||||||
|
def reinitialize_command (self, command, reinit_subcommands=0):
|
||||||
|
cmd = self.distribution.reinitialize_command(
|
||||||
|
command, reinit_subcommands)
|
||||||
|
if command in ('install', 'install_lib'):
|
||||||
|
cmd.install_lib = None # work around distutils bug
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._is_running = True
|
||||||
|
try:
|
||||||
|
_bdist_wininst.run(self)
|
||||||
|
finally:
|
||||||
|
self._is_running = False
|
||||||
|
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
from distutils.command.build_ext import build_ext as _du_build_ext
|
||||||
|
try:
|
||||||
|
# Attempt to use Pyrex for building extensions, if available
|
||||||
|
from Pyrex.Distutils.build_ext import build_ext as _build_ext
|
||||||
|
except ImportError:
|
||||||
|
_build_ext = _du_build_ext
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
from distutils.file_util import copy_file
|
||||||
|
from setuptools.extension import Library
|
||||||
|
from distutils.ccompiler import new_compiler
|
||||||
|
from distutils.sysconfig import customize_compiler, get_config_var
|
||||||
|
get_config_var("LDSHARED") # make sure _config_vars is initialized
|
||||||
|
from distutils.sysconfig import _config_vars
|
||||||
|
from distutils import log
|
||||||
|
from distutils.errors import *
|
||||||
|
|
||||||
|
have_rtld = False
|
||||||
|
use_stubs = False
|
||||||
|
libtype = 'shared'
|
||||||
|
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
use_stubs = True
|
||||||
|
elif os.name != 'nt':
|
||||||
|
try:
|
||||||
|
from dl import RTLD_NOW
|
||||||
|
have_rtld = True
|
||||||
|
use_stubs = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def if_dl(s):
|
||||||
|
if have_rtld:
|
||||||
|
return s
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class build_ext(_build_ext):
|
||||||
|
def run(self):
|
||||||
|
"""Build extensions in build directory, then copy if --inplace"""
|
||||||
|
old_inplace, self.inplace = self.inplace, 0
|
||||||
|
_build_ext.run(self)
|
||||||
|
self.inplace = old_inplace
|
||||||
|
if old_inplace:
|
||||||
|
self.copy_extensions_to_source()
|
||||||
|
|
||||||
|
def copy_extensions_to_source(self):
|
||||||
|
build_py = self.get_finalized_command('build_py')
|
||||||
|
for ext in self.extensions:
|
||||||
|
fullname = self.get_ext_fullname(ext.name)
|
||||||
|
filename = self.get_ext_filename(fullname)
|
||||||
|
modpath = fullname.split('.')
|
||||||
|
package = '.'.join(modpath[:-1])
|
||||||
|
package_dir = build_py.get_package_dir(package)
|
||||||
|
dest_filename = os.path.join(package_dir,os.path.basename(filename))
|
||||||
|
src_filename = os.path.join(self.build_lib,filename)
|
||||||
|
|
||||||
|
# Always copy, even if source is older than destination, to ensure
|
||||||
|
# that the right extensions for the current Python/platform are
|
||||||
|
# used.
|
||||||
|
copy_file(
|
||||||
|
src_filename, dest_filename, verbose=self.verbose,
|
||||||
|
dry_run=self.dry_run
|
||||||
|
)
|
||||||
|
if ext._needs_stub:
|
||||||
|
self.write_stub(package_dir or os.curdir, ext, True)
|
||||||
|
|
||||||
|
|
||||||
|
if _build_ext is not _du_build_ext and not hasattr(_build_ext,'pyrex_sources'):
|
||||||
|
# Workaround for problems using some Pyrex versions w/SWIG and/or 2.4
|
||||||
|
def swig_sources(self, sources, *otherargs):
|
||||||
|
# first do any Pyrex processing
|
||||||
|
sources = _build_ext.swig_sources(self, sources) or sources
|
||||||
|
# Then do any actual SWIG stuff on the remainder
|
||||||
|
return _du_build_ext.swig_sources(self, sources, *otherargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_ext_filename(self, fullname):
|
||||||
|
filename = _build_ext.get_ext_filename(self,fullname)
|
||||||
|
if fullname not in self.ext_map:
|
||||||
|
return filename
|
||||||
|
ext = self.ext_map[fullname]
|
||||||
|
if isinstance(ext,Library):
|
||||||
|
fn, ext = os.path.splitext(filename)
|
||||||
|
return self.shlib_compiler.library_filename(fn,libtype)
|
||||||
|
elif use_stubs and ext._links_to_dynamic:
|
||||||
|
d,fn = os.path.split(filename)
|
||||||
|
return os.path.join(d,'dl-'+fn)
|
||||||
|
else:
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
_build_ext.initialize_options(self)
|
||||||
|
self.shlib_compiler = None
|
||||||
|
self.shlibs = []
|
||||||
|
self.ext_map = {}
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
_build_ext.finalize_options(self)
|
||||||
|
self.extensions = self.extensions or []
|
||||||
|
self.check_extensions_list(self.extensions)
|
||||||
|
self.shlibs = [ext for ext in self.extensions
|
||||||
|
if isinstance(ext,Library)]
|
||||||
|
if self.shlibs:
|
||||||
|
self.setup_shlib_compiler()
|
||||||
|
for ext in self.extensions:
|
||||||
|
ext._full_name = self.get_ext_fullname(ext.name)
|
||||||
|
for ext in self.extensions:
|
||||||
|
fullname = ext._full_name
|
||||||
|
self.ext_map[fullname] = ext
|
||||||
|
|
||||||
|
# distutils 3.1 will also ask for module names
|
||||||
|
# XXX what to do with conflicts?
|
||||||
|
self.ext_map[fullname.split('.')[-1]] = ext
|
||||||
|
|
||||||
|
ltd = ext._links_to_dynamic = \
|
||||||
|
self.shlibs and self.links_to_dynamic(ext) or False
|
||||||
|
ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library)
|
||||||
|
filename = ext._file_name = self.get_ext_filename(fullname)
|
||||||
|
libdir = os.path.dirname(os.path.join(self.build_lib,filename))
|
||||||
|
if ltd and libdir not in ext.library_dirs:
|
||||||
|
ext.library_dirs.append(libdir)
|
||||||
|
if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs:
|
||||||
|
ext.runtime_library_dirs.append(os.curdir)
|
||||||
|
|
||||||
|
def setup_shlib_compiler(self):
|
||||||
|
compiler = self.shlib_compiler = new_compiler(
|
||||||
|
compiler=self.compiler, dry_run=self.dry_run, force=self.force
|
||||||
|
)
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
tmp = _config_vars.copy()
|
||||||
|
try:
|
||||||
|
# XXX Help! I don't have any idea whether these are right...
|
||||||
|
_config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup"
|
||||||
|
_config_vars['CCSHARED'] = " -dynamiclib"
|
||||||
|
_config_vars['SO'] = ".dylib"
|
||||||
|
customize_compiler(compiler)
|
||||||
|
finally:
|
||||||
|
_config_vars.clear()
|
||||||
|
_config_vars.update(tmp)
|
||||||
|
else:
|
||||||
|
customize_compiler(compiler)
|
||||||
|
|
||||||
|
if self.include_dirs is not None:
|
||||||
|
compiler.set_include_dirs(self.include_dirs)
|
||||||
|
if self.define is not None:
|
||||||
|
# 'define' option is a list of (name,value) tuples
|
||||||
|
for (name,value) in self.define:
|
||||||
|
compiler.define_macro(name, value)
|
||||||
|
if self.undef is not None:
|
||||||
|
for macro in self.undef:
|
||||||
|
compiler.undefine_macro(macro)
|
||||||
|
if self.libraries is not None:
|
||||||
|
compiler.set_libraries(self.libraries)
|
||||||
|
if self.library_dirs is not None:
|
||||||
|
compiler.set_library_dirs(self.library_dirs)
|
||||||
|
if self.rpath is not None:
|
||||||
|
compiler.set_runtime_library_dirs(self.rpath)
|
||||||
|
if self.link_objects is not None:
|
||||||
|
compiler.set_link_objects(self.link_objects)
|
||||||
|
|
||||||
|
# hack so distutils' build_extension() builds a library instead
|
||||||
|
compiler.link_shared_object = link_shared_object.__get__(compiler)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_export_symbols(self, ext):
|
||||||
|
if isinstance(ext,Library):
|
||||||
|
return ext.export_symbols
|
||||||
|
return _build_ext.get_export_symbols(self,ext)
|
||||||
|
|
||||||
|
def build_extension(self, ext):
|
||||||
|
_compiler = self.compiler
|
||||||
|
try:
|
||||||
|
if isinstance(ext,Library):
|
||||||
|
self.compiler = self.shlib_compiler
|
||||||
|
_build_ext.build_extension(self,ext)
|
||||||
|
if ext._needs_stub:
|
||||||
|
self.write_stub(
|
||||||
|
self.get_finalized_command('build_py').build_lib, ext
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
self.compiler = _compiler
|
||||||
|
|
||||||
|
def links_to_dynamic(self, ext):
|
||||||
|
"""Return true if 'ext' links to a dynamic lib in the same package"""
|
||||||
|
# XXX this should check to ensure the lib is actually being built
|
||||||
|
# XXX as dynamic, and not just using a locally-found version or a
|
||||||
|
# XXX static-compiled version
|
||||||
|
libnames = dict.fromkeys([lib._full_name for lib in self.shlibs])
|
||||||
|
pkg = '.'.join(ext._full_name.split('.')[:-1]+[''])
|
||||||
|
for libname in ext.libraries:
|
||||||
|
if pkg+libname in libnames: return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_outputs(self):
|
||||||
|
outputs = _build_ext.get_outputs(self)
|
||||||
|
optimize = self.get_finalized_command('build_py').optimize
|
||||||
|
for ext in self.extensions:
|
||||||
|
if ext._needs_stub:
|
||||||
|
base = os.path.join(self.build_lib, *ext._full_name.split('.'))
|
||||||
|
outputs.append(base+'.py')
|
||||||
|
outputs.append(base+'.pyc')
|
||||||
|
if optimize:
|
||||||
|
outputs.append(base+'.pyo')
|
||||||
|
return outputs
|
||||||
|
|
||||||
|
def write_stub(self, output_dir, ext, compile=False):
|
||||||
|
log.info("writing stub loader for %s to %s",ext._full_name, output_dir)
|
||||||
|
stub_file = os.path.join(output_dir, *ext._full_name.split('.'))+'.py'
|
||||||
|
if compile and os.path.exists(stub_file):
|
||||||
|
raise DistutilsError(stub_file+" already exists! Please delete.")
|
||||||
|
if not self.dry_run:
|
||||||
|
f = open(stub_file,'w')
|
||||||
|
f.write('\n'.join([
|
||||||
|
"def __bootstrap__():",
|
||||||
|
" global __bootstrap__, __file__, __loader__",
|
||||||
|
" import sys, os, pkg_resources, imp"+if_dl(", dl"),
|
||||||
|
" __file__ = pkg_resources.resource_filename(__name__,%r)"
|
||||||
|
% os.path.basename(ext._file_name),
|
||||||
|
" del __bootstrap__",
|
||||||
|
" if '__loader__' in globals():",
|
||||||
|
" del __loader__",
|
||||||
|
if_dl(" old_flags = sys.getdlopenflags()"),
|
||||||
|
" old_dir = os.getcwd()",
|
||||||
|
" try:",
|
||||||
|
" os.chdir(os.path.dirname(__file__))",
|
||||||
|
if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
|
||||||
|
" imp.load_dynamic(__name__,__file__)",
|
||||||
|
" finally:",
|
||||||
|
if_dl(" sys.setdlopenflags(old_flags)"),
|
||||||
|
" os.chdir(old_dir)",
|
||||||
|
"__bootstrap__()",
|
||||||
|
"" # terminal \n
|
||||||
|
]))
|
||||||
|
f.close()
|
||||||
|
if compile:
|
||||||
|
from distutils.util import byte_compile
|
||||||
|
byte_compile([stub_file], optimize=0,
|
||||||
|
force=True, dry_run=self.dry_run)
|
||||||
|
optimize = self.get_finalized_command('install_lib').optimize
|
||||||
|
if optimize > 0:
|
||||||
|
byte_compile([stub_file], optimize=optimize,
|
||||||
|
force=True, dry_run=self.dry_run)
|
||||||
|
if os.path.exists(stub_file) and not self.dry_run:
|
||||||
|
os.unlink(stub_file)
|
||||||
|
|
||||||
|
|
||||||
|
if use_stubs or os.name=='nt':
|
||||||
|
# Build shared libraries
|
||||||
|
#
|
||||||
|
def link_shared_object(self, objects, output_libname, output_dir=None,
|
||||||
|
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||||
|
export_symbols=None, debug=0, extra_preargs=None,
|
||||||
|
extra_postargs=None, build_temp=None, target_lang=None
|
||||||
|
): self.link(
|
||||||
|
self.SHARED_LIBRARY, objects, output_libname,
|
||||||
|
output_dir, libraries, library_dirs, runtime_library_dirs,
|
||||||
|
export_symbols, debug, extra_preargs, extra_postargs,
|
||||||
|
build_temp, target_lang
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Build static libraries everywhere else
|
||||||
|
libtype = 'static'
|
||||||
|
|
||||||
|
def link_shared_object(self, objects, output_libname, output_dir=None,
|
||||||
|
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||||
|
export_symbols=None, debug=0, extra_preargs=None,
|
||||||
|
extra_postargs=None, build_temp=None, target_lang=None
|
||||||
|
):
|
||||||
|
# XXX we need to either disallow these attrs on Library instances,
|
||||||
|
# or warn/abort here if set, or something...
|
||||||
|
#libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||||
|
#export_symbols=None, extra_preargs=None, extra_postargs=None,
|
||||||
|
#build_temp=None
|
||||||
|
|
||||||
|
assert output_dir is None # distutils build_ext doesn't pass this
|
||||||
|
output_dir,filename = os.path.split(output_libname)
|
||||||
|
basename, ext = os.path.splitext(filename)
|
||||||
|
if self.library_filename("x").startswith('lib'):
|
||||||
|
# strip 'lib' prefix; this is kludgy if some platform uses
|
||||||
|
# a different prefix
|
||||||
|
basename = basename[3:]
|
||||||
|
|
||||||
|
self.create_static_lib(
|
||||||
|
objects, basename, output_dir, debug, target_lang
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
import os.path, sys, fnmatch
|
||||||
|
from distutils.command.build_py import build_py as _build_py
|
||||||
|
from distutils.util import convert_path
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
|
try:
|
||||||
|
from distutils.util import Mixin2to3 as _Mixin2to3
|
||||||
|
# add support for converting doctests that is missing in 3.1 distutils
|
||||||
|
from distutils import log
|
||||||
|
from lib2to3.refactor import RefactoringTool, get_fixers_from_package
|
||||||
|
import setuptools
|
||||||
|
class DistutilsRefactoringTool(RefactoringTool):
|
||||||
|
def log_error(self, msg, *args, **kw):
|
||||||
|
log.error(msg, *args)
|
||||||
|
|
||||||
|
def log_message(self, msg, *args):
|
||||||
|
log.info(msg, *args)
|
||||||
|
|
||||||
|
def log_debug(self, msg, *args):
|
||||||
|
log.debug(msg, *args)
|
||||||
|
|
||||||
|
class Mixin2to3(_Mixin2to3):
|
||||||
|
def run_2to3(self, files, doctests = False):
|
||||||
|
# See of the distribution option has been set, otherwise check the
|
||||||
|
# setuptools default.
|
||||||
|
if self.distribution.use_2to3 is not True:
|
||||||
|
return
|
||||||
|
if not files:
|
||||||
|
return
|
||||||
|
log.info("Fixing "+" ".join(files))
|
||||||
|
self.__build_fixer_names()
|
||||||
|
self.__exclude_fixers()
|
||||||
|
if doctests:
|
||||||
|
if setuptools.run_2to3_on_doctests:
|
||||||
|
r = DistutilsRefactoringTool(self.fixer_names)
|
||||||
|
r.refactor(files, write=True, doctests_only=True)
|
||||||
|
else:
|
||||||
|
_Mixin2to3.run_2to3(self, files)
|
||||||
|
|
||||||
|
def __build_fixer_names(self):
|
||||||
|
if self.fixer_names: return
|
||||||
|
self.fixer_names = []
|
||||||
|
for p in setuptools.lib2to3_fixer_packages:
|
||||||
|
self.fixer_names.extend(get_fixers_from_package(p))
|
||||||
|
if self.distribution.use_2to3_fixers is not None:
|
||||||
|
for p in self.distribution.use_2to3_fixers:
|
||||||
|
self.fixer_names.extend(get_fixers_from_package(p))
|
||||||
|
|
||||||
|
def __exclude_fixers(self):
|
||||||
|
excluded_fixers = getattr(self, 'exclude_fixers', [])
|
||||||
|
if self.distribution.use_2to3_exclude_fixers is not None:
|
||||||
|
excluded_fixers.extend(self.distribution.use_2to3_exclude_fixers)
|
||||||
|
for fixer_name in excluded_fixers:
|
||||||
|
if fixer_name in self.fixer_names:
|
||||||
|
self.fixer_names.remove(fixer_name)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
class Mixin2to3:
|
||||||
|
def run_2to3(self, files, doctests=True):
|
||||||
|
# Nothing done in 2.x
|
||||||
|
pass
|
||||||
|
|
||||||
|
class build_py(_build_py, Mixin2to3):
|
||||||
|
"""Enhanced 'build_py' command that includes data files with packages
|
||||||
|
|
||||||
|
The data files are specified via a 'package_data' argument to 'setup()'.
|
||||||
|
See 'setuptools.dist.Distribution' for more details.
|
||||||
|
|
||||||
|
Also, this version of the 'build_py' command allows you to specify both
|
||||||
|
'py_modules' and 'packages' in the same setup operation.
|
||||||
|
"""
|
||||||
|
def finalize_options(self):
|
||||||
|
_build_py.finalize_options(self)
|
||||||
|
self.package_data = self.distribution.package_data
|
||||||
|
self.exclude_package_data = self.distribution.exclude_package_data or {}
|
||||||
|
if 'data_files' in self.__dict__: del self.__dict__['data_files']
|
||||||
|
self.__updated_files = []
|
||||||
|
self.__doctests_2to3 = []
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Build modules, packages, and copy data files to build directory"""
|
||||||
|
if not self.py_modules and not self.packages:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.py_modules:
|
||||||
|
self.build_modules()
|
||||||
|
|
||||||
|
if self.packages:
|
||||||
|
self.build_packages()
|
||||||
|
self.build_package_data()
|
||||||
|
|
||||||
|
self.run_2to3(self.__updated_files, False)
|
||||||
|
self.run_2to3(self.__updated_files, True)
|
||||||
|
self.run_2to3(self.__doctests_2to3, True)
|
||||||
|
|
||||||
|
# Only compile actual .py files, using our base class' idea of what our
|
||||||
|
# output files are.
|
||||||
|
self.byte_compile(_build_py.get_outputs(self, include_bytecode=0))
|
||||||
|
|
||||||
|
def __getattr__(self,attr):
|
||||||
|
if attr=='data_files': # lazily compute data files
|
||||||
|
self.data_files = files = self._get_data_files(); return files
|
||||||
|
return _build_py.__getattr__(self,attr)
|
||||||
|
|
||||||
|
def build_module(self, module, module_file, package):
|
||||||
|
outfile, copied = _build_py.build_module(self, module, module_file, package)
|
||||||
|
if copied:
|
||||||
|
self.__updated_files.append(outfile)
|
||||||
|
return outfile, copied
|
||||||
|
|
||||||
|
def _get_data_files(self):
|
||||||
|
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
|
||||||
|
self.analyze_manifest()
|
||||||
|
data = []
|
||||||
|
for package in self.packages or ():
|
||||||
|
# Locate package source directory
|
||||||
|
src_dir = self.get_package_dir(package)
|
||||||
|
|
||||||
|
# Compute package build directory
|
||||||
|
build_dir = os.path.join(*([self.build_lib] + package.split('.')))
|
||||||
|
|
||||||
|
# Length of path to strip from found files
|
||||||
|
plen = len(src_dir)+1
|
||||||
|
|
||||||
|
# Strip directory from globbed filenames
|
||||||
|
filenames = [
|
||||||
|
file[plen:] for file in self.find_data_files(package, src_dir)
|
||||||
|
]
|
||||||
|
data.append( (package, src_dir, build_dir, filenames) )
|
||||||
|
return data
|
||||||
|
|
||||||
|
def find_data_files(self, package, src_dir):
|
||||||
|
"""Return filenames for package's data files in 'src_dir'"""
|
||||||
|
globs = (self.package_data.get('', [])
|
||||||
|
+ self.package_data.get(package, []))
|
||||||
|
files = self.manifest_files.get(package, [])[:]
|
||||||
|
for pattern in globs:
|
||||||
|
# Each pattern has to be converted to a platform-specific path
|
||||||
|
files.extend(glob(os.path.join(src_dir, convert_path(pattern))))
|
||||||
|
return self.exclude_data_files(package, src_dir, files)
|
||||||
|
|
||||||
|
def build_package_data(self):
|
||||||
|
"""Copy data files into build directory"""
|
||||||
|
lastdir = None
|
||||||
|
for package, src_dir, build_dir, filenames in self.data_files:
|
||||||
|
for filename in filenames:
|
||||||
|
target = os.path.join(build_dir, filename)
|
||||||
|
self.mkpath(os.path.dirname(target))
|
||||||
|
srcfile = os.path.join(src_dir, filename)
|
||||||
|
outf, copied = self.copy_file(srcfile, target)
|
||||||
|
srcfile = os.path.abspath(srcfile)
|
||||||
|
if copied and srcfile in self.distribution.convert_2to3_doctests:
|
||||||
|
self.__doctests_2to3.append(outf)
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_manifest(self):
|
||||||
|
self.manifest_files = mf = {}
|
||||||
|
if not self.distribution.include_package_data:
|
||||||
|
return
|
||||||
|
src_dirs = {}
|
||||||
|
for package in self.packages or ():
|
||||||
|
# Locate package source directory
|
||||||
|
src_dirs[assert_relative(self.get_package_dir(package))] = package
|
||||||
|
|
||||||
|
self.run_command('egg_info')
|
||||||
|
ei_cmd = self.get_finalized_command('egg_info')
|
||||||
|
for path in ei_cmd.filelist.files:
|
||||||
|
d,f = os.path.split(assert_relative(path))
|
||||||
|
prev = None
|
||||||
|
oldf = f
|
||||||
|
while d and d!=prev and d not in src_dirs:
|
||||||
|
prev = d
|
||||||
|
d, df = os.path.split(d)
|
||||||
|
f = os.path.join(df, f)
|
||||||
|
if d in src_dirs:
|
||||||
|
if path.endswith('.py') and f==oldf:
|
||||||
|
continue # it's a module, not data
|
||||||
|
mf.setdefault(src_dirs[d],[]).append(path)
|
||||||
|
|
||||||
|
def get_data_files(self): pass # kludge 2.4 for lazy computation
|
||||||
|
|
||||||
|
if sys.version<"2.4": # Python 2.4 already has this code
|
||||||
|
def get_outputs(self, include_bytecode=1):
|
||||||
|
"""Return complete list of files copied to the build directory
|
||||||
|
|
||||||
|
This includes both '.py' files and data files, as well as '.pyc'
|
||||||
|
and '.pyo' files if 'include_bytecode' is true. (This method is
|
||||||
|
needed for the 'install_lib' command to do its job properly, and to
|
||||||
|
generate a correct installation manifest.)
|
||||||
|
"""
|
||||||
|
return _build_py.get_outputs(self, include_bytecode) + [
|
||||||
|
os.path.join(build_dir, filename)
|
||||||
|
for package, src_dir, build_dir,filenames in self.data_files
|
||||||
|
for filename in filenames
|
||||||
|
]
|
||||||
|
|
||||||
|
def check_package(self, package, package_dir):
|
||||||
|
"""Check namespace packages' __init__ for declare_namespace"""
|
||||||
|
try:
|
||||||
|
return self.packages_checked[package]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
init_py = _build_py.check_package(self, package, package_dir)
|
||||||
|
self.packages_checked[package] = init_py
|
||||||
|
|
||||||
|
if not init_py or not self.distribution.namespace_packages:
|
||||||
|
return init_py
|
||||||
|
|
||||||
|
for pkg in self.distribution.namespace_packages:
|
||||||
|
if pkg==package or pkg.startswith(package+'.'):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return init_py
|
||||||
|
|
||||||
|
f = open(init_py,'rbU')
|
||||||
|
if 'declare_namespace'.encode() not in f.read():
|
||||||
|
from distutils import log
|
||||||
|
log.warn(
|
||||||
|
"WARNING: %s is a namespace package, but its __init__.py does\n"
|
||||||
|
"not declare_namespace(); setuptools 0.7 will REQUIRE this!\n"
|
||||||
|
'(See the setuptools manual under "Namespace Packages" for '
|
||||||
|
"details.)\n", package
|
||||||
|
)
|
||||||
|
f.close()
|
||||||
|
return init_py
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
self.packages_checked={}
|
||||||
|
_build_py.initialize_options(self)
|
||||||
|
|
||||||
|
|
||||||
|
def get_package_dir(self, package):
|
||||||
|
res = _build_py.get_package_dir(self, package)
|
||||||
|
if self.distribution.src_root is not None:
|
||||||
|
return os.path.join(self.distribution.src_root, res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def exclude_data_files(self, package, src_dir, files):
|
||||||
|
"""Filter filenames for package's data files in 'src_dir'"""
|
||||||
|
globs = (self.exclude_package_data.get('', [])
|
||||||
|
+ self.exclude_package_data.get(package, []))
|
||||||
|
bad = []
|
||||||
|
for pattern in globs:
|
||||||
|
bad.extend(
|
||||||
|
fnmatch.filter(
|
||||||
|
files, os.path.join(src_dir, convert_path(pattern))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
bad = dict.fromkeys(bad)
|
||||||
|
seen = {}
|
||||||
|
return [
|
||||||
|
f for f in files if f not in bad
|
||||||
|
and f not in seen and seen.setdefault(f,1) # ditch dupes
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def assert_relative(path):
|
||||||
|
if not os.path.isabs(path):
|
||||||
|
return path
|
||||||
|
from distutils.errors import DistutilsSetupError
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"""Error: setup script specifies an absolute path:
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
setup() arguments must *always* be /-separated paths relative to the
|
||||||
|
setup.py directory, *never* absolute paths.
|
||||||
|
""" % path
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
from setuptools.command.easy_install import easy_install
|
||||||
|
from distutils.util import convert_path, subst_vars
|
||||||
|
from pkg_resources import Distribution, PathMetadata, normalize_path
|
||||||
|
from distutils import log
|
||||||
|
from distutils.errors import DistutilsError, DistutilsOptionError
|
||||||
|
import os, sys, setuptools, glob
|
||||||
|
|
||||||
|
class develop(easy_install):
|
||||||
|
"""Set up package for development"""
|
||||||
|
|
||||||
|
description = "install package in 'development mode'"
|
||||||
|
|
||||||
|
user_options = easy_install.user_options + [
|
||||||
|
("uninstall", "u", "Uninstall this source package"),
|
||||||
|
("egg-path=", None, "Set the path to be used in the .egg-link file"),
|
||||||
|
]
|
||||||
|
|
||||||
|
boolean_options = easy_install.boolean_options + ['uninstall']
|
||||||
|
|
||||||
|
command_consumes_arguments = False # override base
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.uninstall:
|
||||||
|
self.multi_version = True
|
||||||
|
self.uninstall_link()
|
||||||
|
else:
|
||||||
|
self.install_for_development()
|
||||||
|
self.warn_deprecated_options()
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
self.uninstall = None
|
||||||
|
self.egg_path = None
|
||||||
|
easy_install.initialize_options(self)
|
||||||
|
self.setup_path = None
|
||||||
|
self.always_copy_from = '.' # always copy eggs installed in curdir
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
ei = self.get_finalized_command("egg_info")
|
||||||
|
if ei.broken_egg_info:
|
||||||
|
raise DistutilsError(
|
||||||
|
"Please rename %r to %r before using 'develop'"
|
||||||
|
% (ei.egg_info, ei.broken_egg_info)
|
||||||
|
)
|
||||||
|
self.args = [ei.egg_name]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
easy_install.finalize_options(self)
|
||||||
|
self.expand_basedirs()
|
||||||
|
self.expand_dirs()
|
||||||
|
# pick up setup-dir .egg files only: no .egg-info
|
||||||
|
self.package_index.scan(glob.glob('*.egg'))
|
||||||
|
|
||||||
|
self.egg_link = os.path.join(self.install_dir, ei.egg_name+'.egg-link')
|
||||||
|
self.egg_base = ei.egg_base
|
||||||
|
if self.egg_path is None:
|
||||||
|
self.egg_path = os.path.abspath(ei.egg_base)
|
||||||
|
|
||||||
|
target = normalize_path(self.egg_base)
|
||||||
|
if normalize_path(os.path.join(self.install_dir, self.egg_path)) != target:
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"--egg-path must be a relative path from the install"
|
||||||
|
" directory to "+target
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make a distribution for the package's source
|
||||||
|
self.dist = Distribution(
|
||||||
|
target,
|
||||||
|
PathMetadata(target, os.path.abspath(ei.egg_info)),
|
||||||
|
project_name = ei.egg_name
|
||||||
|
)
|
||||||
|
|
||||||
|
p = self.egg_base.replace(os.sep,'/')
|
||||||
|
if p!= os.curdir:
|
||||||
|
p = '../' * (p.count('/')+1)
|
||||||
|
self.setup_path = p
|
||||||
|
p = normalize_path(os.path.join(self.install_dir, self.egg_path, p))
|
||||||
|
if p != normalize_path(os.curdir):
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"Can't get a consistent path to setup script from"
|
||||||
|
" installation directory", p, normalize_path(os.curdir))
|
||||||
|
|
||||||
|
def install_for_development(self):
|
||||||
|
if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
|
||||||
|
# If we run 2to3 we can not do this inplace:
|
||||||
|
|
||||||
|
# Ensure metadata is up-to-date
|
||||||
|
self.reinitialize_command('build_py', inplace=0)
|
||||||
|
self.run_command('build_py')
|
||||||
|
bpy_cmd = self.get_finalized_command("build_py")
|
||||||
|
build_path = normalize_path(bpy_cmd.build_lib)
|
||||||
|
|
||||||
|
# Build extensions
|
||||||
|
self.reinitialize_command('egg_info', egg_base=build_path)
|
||||||
|
self.run_command('egg_info')
|
||||||
|
|
||||||
|
self.reinitialize_command('build_ext', inplace=0)
|
||||||
|
self.run_command('build_ext')
|
||||||
|
|
||||||
|
# Fixup egg-link and easy-install.pth
|
||||||
|
ei_cmd = self.get_finalized_command("egg_info")
|
||||||
|
self.egg_path = build_path
|
||||||
|
self.dist.location = build_path
|
||||||
|
self.dist._provider = PathMetadata(build_path, ei_cmd.egg_info) # XXX
|
||||||
|
else:
|
||||||
|
# Without 2to3 inplace works fine:
|
||||||
|
self.run_command('egg_info')
|
||||||
|
|
||||||
|
# Build extensions in-place
|
||||||
|
self.reinitialize_command('build_ext', inplace=1)
|
||||||
|
self.run_command('build_ext')
|
||||||
|
|
||||||
|
self.install_site_py() # ensure that target dir is site-safe
|
||||||
|
if setuptools.bootstrap_install_from:
|
||||||
|
self.easy_install(setuptools.bootstrap_install_from)
|
||||||
|
setuptools.bootstrap_install_from = None
|
||||||
|
|
||||||
|
# create an .egg-link in the installation dir, pointing to our egg
|
||||||
|
log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
|
||||||
|
if not self.dry_run:
|
||||||
|
f = open(self.egg_link,"w")
|
||||||
|
f.write(self.egg_path + "\n" + self.setup_path)
|
||||||
|
f.close()
|
||||||
|
# postprocess the installed distro, fixing up .pth, installing scripts,
|
||||||
|
# and handling requirements
|
||||||
|
self.process_distribution(None, self.dist, not self.no_deps)
|
||||||
|
|
||||||
|
|
||||||
|
def uninstall_link(self):
|
||||||
|
if os.path.exists(self.egg_link):
|
||||||
|
log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
|
||||||
|
egg_link_file = open(self.egg_link)
|
||||||
|
contents = [line.rstrip() for line in egg_link_file]
|
||||||
|
egg_link_file.close()
|
||||||
|
if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
|
||||||
|
log.warn("Link points to %s: uninstall aborted", contents)
|
||||||
|
return
|
||||||
|
if not self.dry_run:
|
||||||
|
os.unlink(self.egg_link)
|
||||||
|
if not self.dry_run:
|
||||||
|
self.update_pth(self.dist) # remove any .pth link to us
|
||||||
|
if self.distribution.scripts:
|
||||||
|
# XXX should also check for entry point scripts!
|
||||||
|
log.warn("Note: you must uninstall or replace scripts manually!")
|
||||||
|
|
||||||
|
def install_egg_scripts(self, dist):
|
||||||
|
if dist is not self.dist:
|
||||||
|
# Installing a dependency, so fall back to normal behavior
|
||||||
|
return easy_install.install_egg_scripts(self,dist)
|
||||||
|
|
||||||
|
# create wrapper scripts in the script dir, pointing to dist.scripts
|
||||||
|
|
||||||
|
# new-style...
|
||||||
|
self.install_wrapper_scripts(dist)
|
||||||
|
|
||||||
|
# ...and old-style
|
||||||
|
for script_name in self.distribution.scripts or []:
|
||||||
|
script_path = os.path.abspath(convert_path(script_name))
|
||||||
|
script_name = os.path.basename(script_path)
|
||||||
|
f = open(script_path,'rU')
|
||||||
|
script_text = f.read()
|
||||||
|
f.close()
|
||||||
|
self.install_script(dist, script_name, script_text, script_path)
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,486 @@
|
|||||||
|
"""setuptools.command.egg_info
|
||||||
|
|
||||||
|
Create a distribution's .egg-info directory and contents"""
|
||||||
|
|
||||||
|
# This module should be kept compatible with Python 2.3
|
||||||
|
import os, re, sys
|
||||||
|
from setuptools import Command
|
||||||
|
from distutils.errors import *
|
||||||
|
from distutils import log
|
||||||
|
from setuptools.command.sdist import sdist
|
||||||
|
from distutils.util import convert_path
|
||||||
|
from distutils.filelist import FileList as _FileList
|
||||||
|
from pkg_resources import parse_requirements, safe_name, parse_version, \
|
||||||
|
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename
|
||||||
|
from sdist import walk_revctrl
|
||||||
|
|
||||||
|
class egg_info(Command):
|
||||||
|
description = "create a distribution's .egg-info directory"
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('egg-base=', 'e', "directory containing .egg-info directories"
|
||||||
|
" (default: top of the source tree)"),
|
||||||
|
('tag-svn-revision', 'r',
|
||||||
|
"Add subversion revision ID to version number"),
|
||||||
|
('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
|
||||||
|
('tag-build=', 'b', "Specify explicit tag to add to version number"),
|
||||||
|
('no-svn-revision', 'R',
|
||||||
|
"Don't add subversion revision ID [default]"),
|
||||||
|
('no-date', 'D', "Don't include date stamp [default]"),
|
||||||
|
]
|
||||||
|
|
||||||
|
boolean_options = ['tag-date', 'tag-svn-revision']
|
||||||
|
negative_opt = {'no-svn-revision': 'tag-svn-revision',
|
||||||
|
'no-date': 'tag-date'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
self.egg_name = None
|
||||||
|
self.egg_version = None
|
||||||
|
self.egg_base = None
|
||||||
|
self.egg_info = None
|
||||||
|
self.tag_build = None
|
||||||
|
self.tag_svn_revision = 0
|
||||||
|
self.tag_date = 0
|
||||||
|
self.broken_egg_info = False
|
||||||
|
self.vtags = None
|
||||||
|
|
||||||
|
def save_version_info(self, filename):
|
||||||
|
from setopt import edit_config
|
||||||
|
edit_config(
|
||||||
|
filename,
|
||||||
|
{'egg_info':
|
||||||
|
{'tag_svn_revision':0, 'tag_date': 0, 'tag_build': self.tags()}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_options (self):
|
||||||
|
self.egg_name = safe_name(self.distribution.get_name())
|
||||||
|
self.vtags = self.tags()
|
||||||
|
self.egg_version = self.tagged_version()
|
||||||
|
|
||||||
|
try:
|
||||||
|
list(
|
||||||
|
parse_requirements('%s==%s' % (self.egg_name,self.egg_version))
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"Invalid distribution name or version syntax: %s-%s" %
|
||||||
|
(self.egg_name,self.egg_version)
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.egg_base is None:
|
||||||
|
dirs = self.distribution.package_dir
|
||||||
|
self.egg_base = (dirs or {}).get('',os.curdir)
|
||||||
|
|
||||||
|
self.ensure_dirname('egg_base')
|
||||||
|
self.egg_info = to_filename(self.egg_name)+'.egg-info'
|
||||||
|
if self.egg_base != os.curdir:
|
||||||
|
self.egg_info = os.path.join(self.egg_base, self.egg_info)
|
||||||
|
if '-' in self.egg_name: self.check_broken_egg_info()
|
||||||
|
|
||||||
|
# Set package version for the benefit of dumber commands
|
||||||
|
# (e.g. sdist, bdist_wininst, etc.)
|
||||||
|
#
|
||||||
|
self.distribution.metadata.version = self.egg_version
|
||||||
|
|
||||||
|
# If we bootstrapped around the lack of a PKG-INFO, as might be the
|
||||||
|
# case in a fresh checkout, make sure that any special tags get added
|
||||||
|
# to the version info
|
||||||
|
#
|
||||||
|
pd = self.distribution._patched_dist
|
||||||
|
if pd is not None and pd.key==self.egg_name.lower():
|
||||||
|
pd._version = self.egg_version
|
||||||
|
pd._parsed_version = parse_version(self.egg_version)
|
||||||
|
self.distribution._patched_dist = None
|
||||||
|
|
||||||
|
|
||||||
|
def write_or_delete_file(self, what, filename, data, force=False):
|
||||||
|
"""Write `data` to `filename` or delete if empty
|
||||||
|
|
||||||
|
If `data` is non-empty, this routine is the same as ``write_file()``.
|
||||||
|
If `data` is empty but not ``None``, this is the same as calling
|
||||||
|
``delete_file(filename)`. If `data` is ``None``, then this is a no-op
|
||||||
|
unless `filename` exists, in which case a warning is issued about the
|
||||||
|
orphaned file (if `force` is false), or deleted (if `force` is true).
|
||||||
|
"""
|
||||||
|
if data:
|
||||||
|
self.write_file(what, filename, data)
|
||||||
|
elif os.path.exists(filename):
|
||||||
|
if data is None and not force:
|
||||||
|
log.warn(
|
||||||
|
"%s not set in setup(), but %s exists", what, filename
|
||||||
|
)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.delete_file(filename)
|
||||||
|
|
||||||
|
def write_file(self, what, filename, data):
|
||||||
|
"""Write `data` to `filename` (if not a dry run) after announcing it
|
||||||
|
|
||||||
|
`what` is used in a log message to identify what is being written
|
||||||
|
to the file.
|
||||||
|
"""
|
||||||
|
log.info("writing %s to %s", what, filename)
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
data = data.encode("utf-8")
|
||||||
|
if not self.dry_run:
|
||||||
|
f = open(filename, 'wb')
|
||||||
|
f.write(data)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def delete_file(self, filename):
|
||||||
|
"""Delete `filename` (if not a dry run) after announcing it"""
|
||||||
|
log.info("deleting %s", filename)
|
||||||
|
if not self.dry_run:
|
||||||
|
os.unlink(filename)
|
||||||
|
|
||||||
|
def tagged_version(self):
|
||||||
|
version = self.distribution.get_version()
|
||||||
|
# egg_info may be called more than once for a distribution,
|
||||||
|
# in which case the version string already contains all tags.
|
||||||
|
if self.vtags and version.endswith(self.vtags):
|
||||||
|
return safe_version(version)
|
||||||
|
return safe_version(version + self.vtags)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.mkpath(self.egg_info)
|
||||||
|
installer = self.distribution.fetch_build_egg
|
||||||
|
for ep in iter_entry_points('egg_info.writers'):
|
||||||
|
writer = ep.load(installer=installer)
|
||||||
|
writer(self, ep.name, os.path.join(self.egg_info,ep.name))
|
||||||
|
|
||||||
|
# Get rid of native_libs.txt if it was put there by older bdist_egg
|
||||||
|
nl = os.path.join(self.egg_info, "native_libs.txt")
|
||||||
|
if os.path.exists(nl):
|
||||||
|
self.delete_file(nl)
|
||||||
|
|
||||||
|
self.find_sources()
|
||||||
|
|
||||||
|
def tags(self):
|
||||||
|
version = ''
|
||||||
|
if self.tag_build:
|
||||||
|
version+=self.tag_build
|
||||||
|
if self.tag_svn_revision and (
|
||||||
|
os.path.exists('.svn') or os.path.exists('PKG-INFO')
|
||||||
|
): version += '-r%s' % self.get_svn_revision()
|
||||||
|
if self.tag_date:
|
||||||
|
import time; version += time.strftime("-%Y%m%d")
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_svn_revision(self):
|
||||||
|
revision = 0
|
||||||
|
urlre = re.compile('url="([^"]+)"')
|
||||||
|
revre = re.compile('committed-rev="(\d+)"')
|
||||||
|
|
||||||
|
for base,dirs,files in os.walk(os.curdir):
|
||||||
|
if '.svn' not in dirs:
|
||||||
|
dirs[:] = []
|
||||||
|
continue # no sense walking uncontrolled subdirs
|
||||||
|
dirs.remove('.svn')
|
||||||
|
f = open(os.path.join(base,'.svn','entries'))
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
if data.startswith('10') or data.startswith('9') or data.startswith('8'):
|
||||||
|
data = map(str.splitlines,data.split('\n\x0c\n'))
|
||||||
|
del data[0][0] # get rid of the '8' or '9' or '10'
|
||||||
|
dirurl = data[0][3]
|
||||||
|
localrev = max([int(d[9]) for d in data if len(d)>9 and d[9]]+[0])
|
||||||
|
elif data.startswith('<?xml'):
|
||||||
|
dirurl = urlre.search(data).group(1) # get repository URL
|
||||||
|
localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0])
|
||||||
|
else:
|
||||||
|
log.warn("unrecognized .svn/entries format; skipping %s", base)
|
||||||
|
dirs[:] = []
|
||||||
|
continue
|
||||||
|
if base==os.curdir:
|
||||||
|
base_url = dirurl+'/' # save the root url
|
||||||
|
elif not dirurl.startswith(base_url):
|
||||||
|
dirs[:] = []
|
||||||
|
continue # not part of the same svn tree, skip it
|
||||||
|
revision = max(revision, localrev)
|
||||||
|
|
||||||
|
return str(revision or get_pkg_info_revision())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def find_sources(self):
|
||||||
|
"""Generate SOURCES.txt manifest file"""
|
||||||
|
manifest_filename = os.path.join(self.egg_info,"SOURCES.txt")
|
||||||
|
mm = manifest_maker(self.distribution)
|
||||||
|
mm.manifest = manifest_filename
|
||||||
|
mm.run()
|
||||||
|
self.filelist = mm.filelist
|
||||||
|
|
||||||
|
def check_broken_egg_info(self):
|
||||||
|
bei = self.egg_name+'.egg-info'
|
||||||
|
if self.egg_base != os.curdir:
|
||||||
|
bei = os.path.join(self.egg_base, bei)
|
||||||
|
if os.path.exists(bei):
|
||||||
|
log.warn(
|
||||||
|
"-"*78+'\n'
|
||||||
|
"Note: Your current .egg-info directory has a '-' in its name;"
|
||||||
|
'\nthis will not work correctly with "setup.py develop".\n\n'
|
||||||
|
'Please rename %s to %s to correct this problem.\n'+'-'*78,
|
||||||
|
bei, self.egg_info
|
||||||
|
)
|
||||||
|
self.broken_egg_info = self.egg_info
|
||||||
|
self.egg_info = bei # make it work for now
|
||||||
|
|
||||||
|
class FileList(_FileList):
|
||||||
|
"""File list that accepts only existing, platform-independent paths"""
|
||||||
|
|
||||||
|
def append(self, item):
|
||||||
|
if item.endswith('\r'): # Fix older sdists built on Windows
|
||||||
|
item = item[:-1]
|
||||||
|
path = convert_path(item)
|
||||||
|
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
try:
|
||||||
|
if os.path.exists(path) or os.path.exists(path.encode('utf-8')):
|
||||||
|
self.files.append(path)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
# Accept UTF-8 filenames even if LANG=C
|
||||||
|
if os.path.exists(path.encode('utf-8')):
|
||||||
|
self.files.append(path)
|
||||||
|
else:
|
||||||
|
log.warn("'%s' not %s encodable -- skipping", path,
|
||||||
|
sys.getfilesystemencoding())
|
||||||
|
else:
|
||||||
|
if os.path.exists(path):
|
||||||
|
self.files.append(path)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class manifest_maker(sdist):
|
||||||
|
|
||||||
|
template = "MANIFEST.in"
|
||||||
|
|
||||||
|
def initialize_options (self):
|
||||||
|
self.use_defaults = 1
|
||||||
|
self.prune = 1
|
||||||
|
self.manifest_only = 1
|
||||||
|
self.force_manifest = 1
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.filelist = FileList()
|
||||||
|
if not os.path.exists(self.manifest):
|
||||||
|
self.write_manifest() # it must exist so it'll get in the list
|
||||||
|
self.filelist.findall()
|
||||||
|
self.add_defaults()
|
||||||
|
if os.path.exists(self.template):
|
||||||
|
self.read_template()
|
||||||
|
self.prune_file_list()
|
||||||
|
self.filelist.sort()
|
||||||
|
self.filelist.remove_duplicates()
|
||||||
|
self.write_manifest()
|
||||||
|
|
||||||
|
def write_manifest (self):
|
||||||
|
"""Write the file list in 'self.filelist' (presumably as filled in
|
||||||
|
by 'add_defaults()' and 'read_template()') to the manifest file
|
||||||
|
named by 'self.manifest'.
|
||||||
|
"""
|
||||||
|
# The manifest must be UTF-8 encodable. See #303.
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
files = []
|
||||||
|
for file in self.filelist.files:
|
||||||
|
try:
|
||||||
|
file.encode("utf-8")
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
log.warn("'%s' not UTF-8 encodable -- skipping" % file)
|
||||||
|
else:
|
||||||
|
files.append(file)
|
||||||
|
self.filelist.files = files
|
||||||
|
|
||||||
|
files = self.filelist.files
|
||||||
|
if os.sep!='/':
|
||||||
|
files = [f.replace(os.sep,'/') for f in files]
|
||||||
|
self.execute(write_file, (self.manifest, files),
|
||||||
|
"writing manifest file '%s'" % self.manifest)
|
||||||
|
|
||||||
|
def warn(self, msg): # suppress missing-file warnings from sdist
|
||||||
|
if not msg.startswith("standard file not found:"):
|
||||||
|
sdist.warn(self, msg)
|
||||||
|
|
||||||
|
def add_defaults(self):
|
||||||
|
sdist.add_defaults(self)
|
||||||
|
self.filelist.append(self.template)
|
||||||
|
self.filelist.append(self.manifest)
|
||||||
|
rcfiles = list(walk_revctrl())
|
||||||
|
if rcfiles:
|
||||||
|
self.filelist.extend(rcfiles)
|
||||||
|
elif os.path.exists(self.manifest):
|
||||||
|
self.read_manifest()
|
||||||
|
ei_cmd = self.get_finalized_command('egg_info')
|
||||||
|
self.filelist.include_pattern("*", prefix=ei_cmd.egg_info)
|
||||||
|
|
||||||
|
def prune_file_list (self):
|
||||||
|
build = self.get_finalized_command('build')
|
||||||
|
base_dir = self.distribution.get_fullname()
|
||||||
|
self.filelist.exclude_pattern(None, prefix=build.build_base)
|
||||||
|
self.filelist.exclude_pattern(None, prefix=base_dir)
|
||||||
|
sep = re.escape(os.sep)
|
||||||
|
self.filelist.exclude_pattern(sep+r'(RCS|CVS|\.svn)'+sep, is_regex=1)
|
||||||
|
|
||||||
|
|
||||||
|
def write_file (filename, contents):
|
||||||
|
"""Create a file with the specified name and write 'contents' (a
|
||||||
|
sequence of strings without line terminators) to it.
|
||||||
|
"""
|
||||||
|
contents = "\n".join(contents)
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
contents = contents.encode("utf-8")
|
||||||
|
f = open(filename, "wb") # always write POSIX-style manifest
|
||||||
|
f.write(contents)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def write_pkg_info(cmd, basename, filename):
|
||||||
|
log.info("writing %s", filename)
|
||||||
|
if not cmd.dry_run:
|
||||||
|
metadata = cmd.distribution.metadata
|
||||||
|
metadata.version, oldver = cmd.egg_version, metadata.version
|
||||||
|
metadata.name, oldname = cmd.egg_name, metadata.name
|
||||||
|
try:
|
||||||
|
# write unescaped data to PKG-INFO, so older pkg_resources
|
||||||
|
# can still parse it
|
||||||
|
metadata.write_pkg_info(cmd.egg_info)
|
||||||
|
finally:
|
||||||
|
metadata.name, metadata.version = oldname, oldver
|
||||||
|
|
||||||
|
safe = getattr(cmd.distribution,'zip_safe',None)
|
||||||
|
import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe)
|
||||||
|
|
||||||
|
def warn_depends_obsolete(cmd, basename, filename):
|
||||||
|
if os.path.exists(filename):
|
||||||
|
log.warn(
|
||||||
|
"WARNING: 'depends.txt' is not used by setuptools 0.6!\n"
|
||||||
|
"Use the install_requires/extras_require setup() args instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def write_requirements(cmd, basename, filename):
|
||||||
|
dist = cmd.distribution
|
||||||
|
data = ['\n'.join(yield_lines(dist.install_requires or ()))]
|
||||||
|
for extra,reqs in (dist.extras_require or {}).items():
|
||||||
|
data.append('\n\n[%s]\n%s' % (extra, '\n'.join(yield_lines(reqs))))
|
||||||
|
cmd.write_or_delete_file("requirements", filename, ''.join(data))
|
||||||
|
|
||||||
|
def write_toplevel_names(cmd, basename, filename):
|
||||||
|
pkgs = dict.fromkeys(
|
||||||
|
[k.split('.',1)[0]
|
||||||
|
for k in cmd.distribution.iter_distribution_names()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
cmd.write_file("top-level names", filename, '\n'.join(pkgs)+'\n')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def overwrite_arg(cmd, basename, filename):
|
||||||
|
write_arg(cmd, basename, filename, True)
|
||||||
|
|
||||||
|
def write_arg(cmd, basename, filename, force=False):
|
||||||
|
argname = os.path.splitext(basename)[0]
|
||||||
|
value = getattr(cmd.distribution, argname, None)
|
||||||
|
if value is not None:
|
||||||
|
value = '\n'.join(value)+'\n'
|
||||||
|
cmd.write_or_delete_file(argname, filename, value, force)
|
||||||
|
|
||||||
|
def write_entries(cmd, basename, filename):
|
||||||
|
ep = cmd.distribution.entry_points
|
||||||
|
|
||||||
|
if isinstance(ep,basestring) or ep is None:
|
||||||
|
data = ep
|
||||||
|
elif ep is not None:
|
||||||
|
data = []
|
||||||
|
for section, contents in ep.items():
|
||||||
|
if not isinstance(contents,basestring):
|
||||||
|
contents = EntryPoint.parse_group(section, contents)
|
||||||
|
contents = '\n'.join(map(str,contents.values()))
|
||||||
|
data.append('[%s]\n%s\n\n' % (section,contents))
|
||||||
|
data = ''.join(data)
|
||||||
|
|
||||||
|
cmd.write_or_delete_file('entry points', filename, data, True)
|
||||||
|
|
||||||
|
def get_pkg_info_revision():
|
||||||
|
# See if we can get a -r### off of PKG-INFO, in case this is an sdist of
|
||||||
|
# a subversion revision
|
||||||
|
#
|
||||||
|
if os.path.exists('PKG-INFO'):
|
||||||
|
f = open('PKG-INFO','rU')
|
||||||
|
for line in f:
|
||||||
|
match = re.match(r"Version:.*-r(\d+)\s*$", line)
|
||||||
|
if match:
|
||||||
|
return int(match.group(1))
|
||||||
|
f.close()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import setuptools, sys, glob
|
||||||
|
from distutils.command.install import install as _install
|
||||||
|
from distutils.errors import DistutilsArgError
|
||||||
|
|
||||||
|
class install(_install):
|
||||||
|
"""Use easy_install to install the package, w/dependencies"""
|
||||||
|
|
||||||
|
user_options = _install.user_options + [
|
||||||
|
('old-and-unmanageable', None, "Try not to use this!"),
|
||||||
|
('single-version-externally-managed', None,
|
||||||
|
"used by system package builders to create 'flat' eggs"),
|
||||||
|
]
|
||||||
|
boolean_options = _install.boolean_options + [
|
||||||
|
'old-and-unmanageable', 'single-version-externally-managed',
|
||||||
|
]
|
||||||
|
new_commands = [
|
||||||
|
('install_egg_info', lambda self: True),
|
||||||
|
('install_scripts', lambda self: True),
|
||||||
|
]
|
||||||
|
_nc = dict(new_commands)
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
_install.initialize_options(self)
|
||||||
|
self.old_and_unmanageable = None
|
||||||
|
self.single_version_externally_managed = None
|
||||||
|
self.no_compile = None # make DISTUTILS_DEBUG work right!
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
_install.finalize_options(self)
|
||||||
|
if self.root:
|
||||||
|
self.single_version_externally_managed = True
|
||||||
|
elif self.single_version_externally_managed:
|
||||||
|
if not self.root and not self.record:
|
||||||
|
raise DistutilsArgError(
|
||||||
|
"You must specify --record or --root when building system"
|
||||||
|
" packages"
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_extra_path(self):
|
||||||
|
if self.root or self.single_version_externally_managed:
|
||||||
|
# explicit backward-compatibility mode, allow extra_path to work
|
||||||
|
return _install.handle_extra_path(self)
|
||||||
|
|
||||||
|
# Ignore extra_path when installing an egg (or being run by another
|
||||||
|
# command without --root or --single-version-externally-managed
|
||||||
|
self.path_file = None
|
||||||
|
self.extra_dirs = ''
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# Explicit request for old-style install? Just do it
|
||||||
|
if self.old_and_unmanageable or self.single_version_externally_managed:
|
||||||
|
return _install.run(self)
|
||||||
|
|
||||||
|
# Attempt to detect whether we were called from setup() or by another
|
||||||
|
# command. If we were called by setup(), our caller will be the
|
||||||
|
# 'run_command' method in 'distutils.dist', and *its* caller will be
|
||||||
|
# the 'run_commands' method. If we were called any other way, our
|
||||||
|
# immediate caller *might* be 'run_command', but it won't have been
|
||||||
|
# called by 'run_commands'. This is slightly kludgy, but seems to
|
||||||
|
# work.
|
||||||
|
#
|
||||||
|
caller = sys._getframe(2)
|
||||||
|
caller_module = caller.f_globals.get('__name__','')
|
||||||
|
caller_name = caller.f_code.co_name
|
||||||
|
|
||||||
|
if caller_module != 'distutils.dist' or caller_name!='run_commands':
|
||||||
|
# We weren't called from the command line or setup(), so we
|
||||||
|
# should run in backward-compatibility mode to support bdist_*
|
||||||
|
# commands.
|
||||||
|
_install.run(self)
|
||||||
|
else:
|
||||||
|
self.do_egg_install()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def do_egg_install(self):
|
||||||
|
|
||||||
|
easy_install = self.distribution.get_command_class('easy_install')
|
||||||
|
|
||||||
|
cmd = easy_install(
|
||||||
|
self.distribution, args="x", root=self.root, record=self.record,
|
||||||
|
)
|
||||||
|
cmd.ensure_finalized() # finalize before bdist_egg munges install cmd
|
||||||
|
cmd.always_copy_from = '.' # make sure local-dir eggs get installed
|
||||||
|
|
||||||
|
# pick up setup-dir .egg files only: no .egg-info
|
||||||
|
cmd.package_index.scan(glob.glob('*.egg'))
|
||||||
|
|
||||||
|
self.run_command('bdist_egg')
|
||||||
|
args = [self.distribution.get_command_obj('bdist_egg').egg_output]
|
||||||
|
|
||||||
|
if setuptools.bootstrap_install_from:
|
||||||
|
# Bootstrap self-installation of setuptools
|
||||||
|
args.insert(0, setuptools.bootstrap_install_from)
|
||||||
|
|
||||||
|
cmd.args = args
|
||||||
|
cmd.run()
|
||||||
|
setuptools.bootstrap_install_from = None
|
||||||
|
|
||||||
|
# XXX Python 3.1 doesn't see _nc if this is inside the class
|
||||||
|
install.sub_commands = [
|
||||||
|
cmd for cmd in _install.sub_commands if cmd[0] not in install._nc
|
||||||
|
] + install.new_commands
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
from setuptools import Command
|
||||||
|
from setuptools.archive_util import unpack_archive
|
||||||
|
from distutils import log, dir_util
|
||||||
|
import os, shutil, pkg_resources
|
||||||
|
|
||||||
|
class install_egg_info(Command):
|
||||||
|
"""Install an .egg-info directory for the package"""
|
||||||
|
|
||||||
|
description = "Install an .egg-info directory for the package"
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('install-dir=', 'd', "directory to install to"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
self.install_dir = None
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
self.set_undefined_options('install_lib',('install_dir','install_dir'))
|
||||||
|
ei_cmd = self.get_finalized_command("egg_info")
|
||||||
|
basename = pkg_resources.Distribution(
|
||||||
|
None, None, ei_cmd.egg_name, ei_cmd.egg_version
|
||||||
|
).egg_name()+'.egg-info'
|
||||||
|
self.source = ei_cmd.egg_info
|
||||||
|
self.target = os.path.join(self.install_dir, basename)
|
||||||
|
self.outputs = [self.target]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.run_command('egg_info')
|
||||||
|
target = self.target
|
||||||
|
if os.path.isdir(self.target) and not os.path.islink(self.target):
|
||||||
|
dir_util.remove_tree(self.target, dry_run=self.dry_run)
|
||||||
|
elif os.path.exists(self.target):
|
||||||
|
self.execute(os.unlink,(self.target,),"Removing "+self.target)
|
||||||
|
if not self.dry_run:
|
||||||
|
pkg_resources.ensure_directory(self.target)
|
||||||
|
self.execute(self.copytree, (),
|
||||||
|
"Copying %s to %s" % (self.source, self.target)
|
||||||
|
)
|
||||||
|
self.install_namespaces()
|
||||||
|
|
||||||
|
def get_outputs(self):
|
||||||
|
return self.outputs
|
||||||
|
|
||||||
|
def copytree(self):
|
||||||
|
# Copy the .egg-info tree to site-packages
|
||||||
|
def skimmer(src,dst):
|
||||||
|
# filter out source-control directories; note that 'src' is always
|
||||||
|
# a '/'-separated path, regardless of platform. 'dst' is a
|
||||||
|
# platform-specific path.
|
||||||
|
for skip in '.svn/','CVS/':
|
||||||
|
if src.startswith(skip) or '/'+skip in src:
|
||||||
|
return None
|
||||||
|
self.outputs.append(dst)
|
||||||
|
log.debug("Copying %s to %s", src, dst)
|
||||||
|
return dst
|
||||||
|
unpack_archive(self.source, self.target, skimmer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def install_namespaces(self):
|
||||||
|
nsp = self._get_all_ns_packages()
|
||||||
|
if not nsp: return
|
||||||
|
filename,ext = os.path.splitext(self.target)
|
||||||
|
filename += '-nspkg.pth'; self.outputs.append(filename)
|
||||||
|
log.info("Installing %s",filename)
|
||||||
|
if not self.dry_run:
|
||||||
|
f = open(filename,'wt')
|
||||||
|
for pkg in nsp:
|
||||||
|
# ensure pkg is not a unicode string under Python 2.7
|
||||||
|
pkg = str(pkg)
|
||||||
|
pth = tuple(pkg.split('.'))
|
||||||
|
trailer = '\n'
|
||||||
|
if '.' in pkg:
|
||||||
|
trailer = (
|
||||||
|
"; m and setattr(sys.modules[%r], %r, m)\n"
|
||||||
|
% ('.'.join(pth[:-1]), pth[-1])
|
||||||
|
)
|
||||||
|
f.write(
|
||||||
|
"import sys,types,os; "
|
||||||
|
"p = os.path.join(sys._getframe(1).f_locals['sitedir'], "
|
||||||
|
"*%(pth)r); "
|
||||||
|
"ie = os.path.exists(os.path.join(p,'__init__.py')); "
|
||||||
|
"m = not ie and "
|
||||||
|
"sys.modules.setdefault(%(pkg)r,types.ModuleType(%(pkg)r)); "
|
||||||
|
"mp = (m or []) and m.__dict__.setdefault('__path__',[]); "
|
||||||
|
"(p not in mp) and mp.append(p)%(trailer)s"
|
||||||
|
% locals()
|
||||||
|
)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def _get_all_ns_packages(self):
|
||||||
|
nsp = {}
|
||||||
|
for pkg in self.distribution.namespace_packages or []:
|
||||||
|
pkg = pkg.split('.')
|
||||||
|
while pkg:
|
||||||
|
nsp['.'.join(pkg)] = 1
|
||||||
|
pkg.pop()
|
||||||
|
nsp=list(nsp)
|
||||||
|
nsp.sort() # set up shorter names first
|
||||||
|
return nsp
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
from distutils.command.install_lib import install_lib as _install_lib
|
||||||
|
import os
|
||||||
|
|
||||||
|
class install_lib(_install_lib):
|
||||||
|
"""Don't add compiled flags to filenames of non-Python files"""
|
||||||
|
|
||||||
|
def _bytecode_filenames (self, py_filenames):
|
||||||
|
bytecode_files = []
|
||||||
|
for py_file in py_filenames:
|
||||||
|
if not py_file.endswith('.py'):
|
||||||
|
continue
|
||||||
|
if self.compile:
|
||||||
|
bytecode_files.append(py_file + "c")
|
||||||
|
if self.optimize > 0:
|
||||||
|
bytecode_files.append(py_file + "o")
|
||||||
|
|
||||||
|
return bytecode_files
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.build()
|
||||||
|
outfiles = self.install()
|
||||||
|
if outfiles is not None:
|
||||||
|
# always compile, in case we have any extension stubs to deal with
|
||||||
|
self.byte_compile(outfiles)
|
||||||
|
|
||||||
|
def get_exclusions(self):
|
||||||
|
exclude = {}
|
||||||
|
nsp = self.distribution.namespace_packages
|
||||||
|
|
||||||
|
if (nsp and self.get_finalized_command('install')
|
||||||
|
.single_version_externally_managed
|
||||||
|
):
|
||||||
|
for pkg in nsp:
|
||||||
|
parts = pkg.split('.')
|
||||||
|
while parts:
|
||||||
|
pkgdir = os.path.join(self.install_dir, *parts)
|
||||||
|
for f in '__init__.py', '__init__.pyc', '__init__.pyo':
|
||||||
|
exclude[os.path.join(pkgdir,f)] = 1
|
||||||
|
parts.pop()
|
||||||
|
return exclude
|
||||||
|
|
||||||
|
def copy_tree(
|
||||||
|
self, infile, outfile,
|
||||||
|
preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1
|
||||||
|
):
|
||||||
|
assert preserve_mode and preserve_times and not preserve_symlinks
|
||||||
|
exclude = self.get_exclusions()
|
||||||
|
|
||||||
|
if not exclude:
|
||||||
|
return _install_lib.copy_tree(self, infile, outfile)
|
||||||
|
|
||||||
|
# Exclude namespace package __init__.py* files from the output
|
||||||
|
|
||||||
|
from setuptools.archive_util import unpack_directory
|
||||||
|
from distutils import log
|
||||||
|
|
||||||
|
outfiles = []
|
||||||
|
|
||||||
|
def pf(src, dst):
|
||||||
|
if dst in exclude:
|
||||||
|
log.warn("Skipping installation of %s (namespace package)",dst)
|
||||||
|
return False
|
||||||
|
|
||||||
|
log.info("copying %s -> %s", src, os.path.dirname(dst))
|
||||||
|
outfiles.append(dst)
|
||||||
|
return dst
|
||||||
|
|
||||||
|
unpack_directory(infile, outfile, pf)
|
||||||
|
return outfiles
|
||||||
|
|
||||||
|
def get_outputs(self):
|
||||||
|
outputs = _install_lib.get_outputs(self)
|
||||||
|
exclude = self.get_exclusions()
|
||||||
|
if exclude:
|
||||||
|
return [f for f in outputs if f not in exclude]
|
||||||
|
return outputs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
from distutils.command.install_scripts import install_scripts \
|
||||||
|
as _install_scripts
|
||||||
|
from pkg_resources import Distribution, PathMetadata, ensure_directory
|
||||||
|
import os
|
||||||
|
from distutils import log
|
||||||
|
|
||||||
|
class install_scripts(_install_scripts):
|
||||||
|
"""Do normal script install, plus any egg_info wrapper scripts"""
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
_install_scripts.initialize_options(self)
|
||||||
|
self.no_ep = False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
from setuptools.command.easy_install import get_script_args
|
||||||
|
from setuptools.command.easy_install import sys_executable
|
||||||
|
|
||||||
|
self.run_command("egg_info")
|
||||||
|
if self.distribution.scripts:
|
||||||
|
_install_scripts.run(self) # run first to set up self.outfiles
|
||||||
|
else:
|
||||||
|
self.outfiles = []
|
||||||
|
if self.no_ep:
|
||||||
|
# don't install entry point scripts into .egg file!
|
||||||
|
return
|
||||||
|
|
||||||
|
ei_cmd = self.get_finalized_command("egg_info")
|
||||||
|
dist = Distribution(
|
||||||
|
ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info),
|
||||||
|
ei_cmd.egg_name, ei_cmd.egg_version,
|
||||||
|
)
|
||||||
|
bs_cmd = self.get_finalized_command('build_scripts')
|
||||||
|
executable = getattr(bs_cmd,'executable',sys_executable)
|
||||||
|
is_wininst = getattr(
|
||||||
|
self.get_finalized_command("bdist_wininst"), '_is_running', False
|
||||||
|
)
|
||||||
|
for args in get_script_args(dist, executable, is_wininst):
|
||||||
|
self.write_script(*args)
|
||||||
|
|
||||||
|
def write_script(self, script_name, contents, mode="t", *ignored):
|
||||||
|
"""Write an executable file to the scripts directory"""
|
||||||
|
from setuptools.command.easy_install import chmod, current_umask
|
||||||
|
log.info("Installing %s script to %s", script_name, self.install_dir)
|
||||||
|
target = os.path.join(self.install_dir, script_name)
|
||||||
|
self.outfiles.append(target)
|
||||||
|
|
||||||
|
mask = current_umask()
|
||||||
|
if not self.dry_run:
|
||||||
|
ensure_directory(target)
|
||||||
|
f = open(target,"w"+mode)
|
||||||
|
f.write(contents)
|
||||||
|
f.close()
|
||||||
|
chmod(target, 0777-mask)
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from distutils.command.register import register as _register
|
||||||
|
|
||||||
|
class register(_register):
|
||||||
|
__doc__ = _register.__doc__
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# Make sure that we are using valid current name/version info
|
||||||
|
self.run_command('egg_info')
|
||||||
|
_register.run(self)
|
||||||
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import distutils, os
|
||||||
|
from setuptools import Command
|
||||||
|
from distutils.util import convert_path
|
||||||
|
from distutils import log
|
||||||
|
from distutils.errors import *
|
||||||
|
|
||||||
|
class rotate(Command):
|
||||||
|
"""Delete older distributions"""
|
||||||
|
|
||||||
|
description = "delete older distributions, keeping N newest files"
|
||||||
|
user_options = [
|
||||||
|
('match=', 'm', "patterns to match (required)"),
|
||||||
|
('dist-dir=', 'd', "directory where the distributions are"),
|
||||||
|
('keep=', 'k', "number of matching distributions to keep"),
|
||||||
|
]
|
||||||
|
|
||||||
|
boolean_options = []
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
self.match = None
|
||||||
|
self.dist_dir = None
|
||||||
|
self.keep = None
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
if self.match is None:
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"Must specify one or more (comma-separated) match patterns "
|
||||||
|
"(e.g. '.zip' or '.egg')"
|
||||||
|
)
|
||||||
|
if self.keep is None:
|
||||||
|
raise DistutilsOptionError("Must specify number of files to keep")
|
||||||
|
try:
|
||||||
|
self.keep = int(self.keep)
|
||||||
|
except ValueError:
|
||||||
|
raise DistutilsOptionError("--keep must be an integer")
|
||||||
|
if isinstance(self.match, basestring):
|
||||||
|
self.match = [
|
||||||
|
convert_path(p.strip()) for p in self.match.split(',')
|
||||||
|
]
|
||||||
|
self.set_undefined_options('bdist',('dist_dir', 'dist_dir'))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.run_command("egg_info")
|
||||||
|
from glob import glob
|
||||||
|
for pattern in self.match:
|
||||||
|
pattern = self.distribution.get_name()+'*'+pattern
|
||||||
|
files = glob(os.path.join(self.dist_dir,pattern))
|
||||||
|
files = [(os.path.getmtime(f),f) for f in files]
|
||||||
|
files.sort()
|
||||||
|
files.reverse()
|
||||||
|
|
||||||
|
log.info("%d file(s) matching %s", len(files), pattern)
|
||||||
|
files = files[self.keep:]
|
||||||
|
for (t,f) in files:
|
||||||
|
log.info("Deleting %s", f)
|
||||||
|
if not self.dry_run:
|
||||||
|
os.unlink(f)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import distutils, os
|
||||||
|
from setuptools import Command
|
||||||
|
from setuptools.command.setopt import edit_config, option_base
|
||||||
|
|
||||||
|
class saveopts(option_base):
|
||||||
|
"""Save command-line options to a file"""
|
||||||
|
|
||||||
|
description = "save supplied options to setup.cfg or other config file"
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
dist = self.distribution
|
||||||
|
commands = dist.command_options.keys()
|
||||||
|
settings = {}
|
||||||
|
|
||||||
|
for cmd in commands:
|
||||||
|
|
||||||
|
if cmd=='saveopts':
|
||||||
|
continue # don't save our own options!
|
||||||
|
|
||||||
|
for opt,(src,val) in dist.get_option_dict(cmd).items():
|
||||||
|
if src=="command line":
|
||||||
|
settings.setdefault(cmd,{})[opt] = val
|
||||||
|
|
||||||
|
edit_config(self.filename, settings, self.dry_run)
|
||||||
|
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
from distutils.command.sdist import sdist as _sdist
|
||||||
|
from distutils.util import convert_path
|
||||||
|
from distutils import log
|
||||||
|
import os, re, sys, pkg_resources
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
|
READMES = ('README', 'README.rst', 'README.txt')
|
||||||
|
|
||||||
|
entities = [
|
||||||
|
("<","<"), (">", ">"), (""", '"'), ("'", "'"),
|
||||||
|
("&", "&")
|
||||||
|
]
|
||||||
|
|
||||||
|
def unescape(data):
|
||||||
|
for old,new in entities:
|
||||||
|
data = data.replace(old,new)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def re_finder(pattern, postproc=None):
|
||||||
|
def find(dirname, filename):
|
||||||
|
f = open(filename,'rU')
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
|
for match in pattern.finditer(data):
|
||||||
|
path = match.group(1)
|
||||||
|
if postproc:
|
||||||
|
path = postproc(path)
|
||||||
|
yield joinpath(dirname,path)
|
||||||
|
return find
|
||||||
|
|
||||||
|
def joinpath(prefix,suffix):
|
||||||
|
if not prefix:
|
||||||
|
return suffix
|
||||||
|
return os.path.join(prefix,suffix)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def walk_revctrl(dirname=''):
|
||||||
|
"""Find all files under revision control"""
|
||||||
|
for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
|
||||||
|
for item in ep.load()(dirname):
|
||||||
|
yield item
|
||||||
|
|
||||||
|
def _default_revctrl(dirname=''):
|
||||||
|
for path, finder in finders:
|
||||||
|
path = joinpath(dirname,path)
|
||||||
|
if os.path.isfile(path):
|
||||||
|
for path in finder(dirname,path):
|
||||||
|
if os.path.isfile(path):
|
||||||
|
yield path
|
||||||
|
elif os.path.isdir(path):
|
||||||
|
for item in _default_revctrl(path):
|
||||||
|
yield item
|
||||||
|
|
||||||
|
def externals_finder(dirname, filename):
|
||||||
|
"""Find any 'svn:externals' directories"""
|
||||||
|
found = False
|
||||||
|
f = open(filename,'rt')
|
||||||
|
for line in iter(f.readline, ''): # can't use direct iter!
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts)==2:
|
||||||
|
kind,length = parts
|
||||||
|
data = f.read(int(length))
|
||||||
|
if kind=='K' and data=='svn:externals':
|
||||||
|
found = True
|
||||||
|
elif kind=='V' and found:
|
||||||
|
f.close()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
f.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
for line in data.splitlines():
|
||||||
|
parts = line.split()
|
||||||
|
if parts:
|
||||||
|
yield joinpath(dirname, parts[0])
|
||||||
|
|
||||||
|
|
||||||
|
entries_pattern = re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I)
|
||||||
|
|
||||||
|
def entries_finder(dirname, filename):
|
||||||
|
f = open(filename,'rU')
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
|
if data.startswith('10') or data.startswith('9') or data.startswith('8'):
|
||||||
|
for record in map(str.splitlines, data.split('\n\x0c\n')[1:]):
|
||||||
|
# subversion 1.6/1.5/1.4
|
||||||
|
if not record or len(record)>=6 and record[5]=="delete":
|
||||||
|
continue # skip deleted
|
||||||
|
yield joinpath(dirname, record[0])
|
||||||
|
elif data.startswith('<?xml'):
|
||||||
|
for match in entries_pattern.finditer(data):
|
||||||
|
yield joinpath(dirname,unescape(match.group(1)))
|
||||||
|
else:
|
||||||
|
log.warn("unrecognized .svn/entries format in %s", os.path.abspath(dirname))
|
||||||
|
|
||||||
|
|
||||||
|
finders = [
|
||||||
|
(convert_path('CVS/Entries'),
|
||||||
|
re_finder(re.compile(r"^\w?/([^/]+)/", re.M))),
|
||||||
|
(convert_path('.svn/entries'), entries_finder),
|
||||||
|
(convert_path('.svn/dir-props'), externals_finder),
|
||||||
|
(convert_path('.svn/dir-prop-base'), externals_finder), # svn 1.4
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class sdist(_sdist):
|
||||||
|
"""Smart sdist that finds anything supported by revision control"""
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('formats=', None,
|
||||||
|
"formats for source distribution (comma-separated list)"),
|
||||||
|
('keep-temp', 'k',
|
||||||
|
"keep the distribution tree around after creating " +
|
||||||
|
"archive file(s)"),
|
||||||
|
('dist-dir=', 'd',
|
||||||
|
"directory to put the source distribution archive(s) in "
|
||||||
|
"[default: dist]"),
|
||||||
|
]
|
||||||
|
|
||||||
|
negative_opt = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.run_command('egg_info')
|
||||||
|
ei_cmd = self.get_finalized_command('egg_info')
|
||||||
|
self.filelist = ei_cmd.filelist
|
||||||
|
self.filelist.append(os.path.join(ei_cmd.egg_info,'SOURCES.txt'))
|
||||||
|
self.check_readme()
|
||||||
|
|
||||||
|
# Run sub commands
|
||||||
|
for cmd_name in self.get_sub_commands():
|
||||||
|
self.run_command(cmd_name)
|
||||||
|
|
||||||
|
# Call check_metadata only if no 'check' command
|
||||||
|
# (distutils <= 2.6)
|
||||||
|
import distutils.command
|
||||||
|
if 'check' not in distutils.command.__all__:
|
||||||
|
self.check_metadata()
|
||||||
|
|
||||||
|
self.make_distribution()
|
||||||
|
|
||||||
|
dist_files = getattr(self.distribution,'dist_files',[])
|
||||||
|
for file in self.archive_files:
|
||||||
|
data = ('sdist', '', file)
|
||||||
|
if data not in dist_files:
|
||||||
|
dist_files.append(data)
|
||||||
|
|
||||||
|
def add_defaults(self):
|
||||||
|
standards = [READMES,
|
||||||
|
self.distribution.script_name]
|
||||||
|
for fn in standards:
|
||||||
|
if isinstance(fn, tuple):
|
||||||
|
alts = fn
|
||||||
|
got_it = 0
|
||||||
|
for fn in alts:
|
||||||
|
if os.path.exists(fn):
|
||||||
|
got_it = 1
|
||||||
|
self.filelist.append(fn)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not got_it:
|
||||||
|
self.warn("standard file not found: should have one of " +
|
||||||
|
', '.join(alts))
|
||||||
|
else:
|
||||||
|
if os.path.exists(fn):
|
||||||
|
self.filelist.append(fn)
|
||||||
|
else:
|
||||||
|
self.warn("standard file '%s' not found" % fn)
|
||||||
|
|
||||||
|
optional = ['test/test*.py', 'setup.cfg']
|
||||||
|
for pattern in optional:
|
||||||
|
files = filter(os.path.isfile, glob(pattern))
|
||||||
|
if files:
|
||||||
|
self.filelist.extend(files)
|
||||||
|
|
||||||
|
# getting python files
|
||||||
|
if self.distribution.has_pure_modules():
|
||||||
|
build_py = self.get_finalized_command('build_py')
|
||||||
|
self.filelist.extend(build_py.get_source_files())
|
||||||
|
# This functionality is incompatible with include_package_data, and
|
||||||
|
# will in fact create an infinite recursion if include_package_data
|
||||||
|
# is True. Use of include_package_data will imply that
|
||||||
|
# distutils-style automatic handling of package_data is disabled
|
||||||
|
if not self.distribution.include_package_data:
|
||||||
|
for _, src_dir, _, filenames in build_py.data_files:
|
||||||
|
self.filelist.extend([os.path.join(src_dir, filename)
|
||||||
|
for filename in filenames])
|
||||||
|
|
||||||
|
if self.distribution.has_ext_modules():
|
||||||
|
build_ext = self.get_finalized_command('build_ext')
|
||||||
|
self.filelist.extend(build_ext.get_source_files())
|
||||||
|
|
||||||
|
if self.distribution.has_c_libraries():
|
||||||
|
build_clib = self.get_finalized_command('build_clib')
|
||||||
|
self.filelist.extend(build_clib.get_source_files())
|
||||||
|
|
||||||
|
if self.distribution.has_scripts():
|
||||||
|
build_scripts = self.get_finalized_command('build_scripts')
|
||||||
|
self.filelist.extend(build_scripts.get_source_files())
|
||||||
|
|
||||||
|
def __read_template_hack(self):
|
||||||
|
# This grody hack closes the template file (MANIFEST.in) if an
|
||||||
|
# exception occurs during read_template.
|
||||||
|
# Doing so prevents an error when easy_install attempts to delete the
|
||||||
|
# file.
|
||||||
|
try:
|
||||||
|
_sdist.read_template(self)
|
||||||
|
except:
|
||||||
|
sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close()
|
||||||
|
raise
|
||||||
|
# Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle
|
||||||
|
# has been fixed, so only override the method if we're using an earlier
|
||||||
|
# Python.
|
||||||
|
if (
|
||||||
|
sys.version_info < (2,7,2)
|
||||||
|
or (3,0) <= sys.version_info < (3,1,4)
|
||||||
|
or (3,2) <= sys.version_info < (3,2,1)
|
||||||
|
):
|
||||||
|
read_template = __read_template_hack
|
||||||
|
|
||||||
|
def check_readme(self):
|
||||||
|
for f in READMES:
|
||||||
|
if os.path.exists(f):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.warn(
|
||||||
|
"standard file not found: should have one of " +', '.join(READMES)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_release_tree(self, base_dir, files):
|
||||||
|
_sdist.make_release_tree(self, base_dir, files)
|
||||||
|
|
||||||
|
# Save any egg_info command line options used to create this sdist
|
||||||
|
dest = os.path.join(base_dir, 'setup.cfg')
|
||||||
|
if hasattr(os,'link') and os.path.exists(dest):
|
||||||
|
# unlink and re-copy, since it might be hard-linked, and
|
||||||
|
# we don't want to change the source version
|
||||||
|
os.unlink(dest)
|
||||||
|
self.copy_file('setup.cfg', dest)
|
||||||
|
|
||||||
|
self.get_finalized_command('egg_info').save_version_info(dest)
|
||||||
|
|
||||||
|
def _manifest_is_not_generated(self):
|
||||||
|
# check for special comment used in 2.7.1 and higher
|
||||||
|
if not os.path.isfile(self.manifest):
|
||||||
|
return False
|
||||||
|
|
||||||
|
fp = open(self.manifest, 'rbU')
|
||||||
|
try:
|
||||||
|
first_line = fp.readline()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
return first_line != '# file GENERATED by distutils, do NOT edit\n'.encode()
|
||||||
|
|
||||||
|
def read_manifest(self):
|
||||||
|
"""Read the manifest file (named by 'self.manifest') and use it to
|
||||||
|
fill in 'self.filelist', the list of files to include in the source
|
||||||
|
distribution.
|
||||||
|
"""
|
||||||
|
log.info("reading manifest file '%s'", self.manifest)
|
||||||
|
manifest = open(self.manifest, 'rbU')
|
||||||
|
for line in manifest:
|
||||||
|
# The manifest must contain UTF-8. See #303.
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
try:
|
||||||
|
line = line.decode('UTF-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
log.warn("%r not UTF-8 decodable -- skipping" % line)
|
||||||
|
continue
|
||||||
|
# ignore comments and blank lines
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith('#') or not line:
|
||||||
|
continue
|
||||||
|
self.filelist.append(line)
|
||||||
|
manifest.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
import distutils, os
|
||||||
|
from setuptools import Command
|
||||||
|
from distutils.util import convert_path
|
||||||
|
from distutils import log
|
||||||
|
from distutils.errors import *
|
||||||
|
|
||||||
|
__all__ = ['config_file', 'edit_config', 'option_base', 'setopt']
|
||||||
|
|
||||||
|
|
||||||
|
def config_file(kind="local"):
|
||||||
|
"""Get the filename of the distutils, local, global, or per-user config
|
||||||
|
|
||||||
|
`kind` must be one of "local", "global", or "user"
|
||||||
|
"""
|
||||||
|
if kind=='local':
|
||||||
|
return 'setup.cfg'
|
||||||
|
if kind=='global':
|
||||||
|
return os.path.join(
|
||||||
|
os.path.dirname(distutils.__file__),'distutils.cfg'
|
||||||
|
)
|
||||||
|
if kind=='user':
|
||||||
|
dot = os.name=='posix' and '.' or ''
|
||||||
|
return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot))
|
||||||
|
raise ValueError(
|
||||||
|
"config_file() type must be 'local', 'global', or 'user'", kind
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def edit_config(filename, settings, dry_run=False):
|
||||||
|
"""Edit a configuration file to include `settings`
|
||||||
|
|
||||||
|
`settings` is a dictionary of dictionaries or ``None`` values, keyed by
|
||||||
|
command/section name. A ``None`` value means to delete the entire section,
|
||||||
|
while a dictionary lists settings to be changed or deleted in that section.
|
||||||
|
A setting of ``None`` means to delete that setting.
|
||||||
|
"""
|
||||||
|
from ConfigParser import RawConfigParser
|
||||||
|
log.debug("Reading configuration from %s", filename)
|
||||||
|
opts = RawConfigParser()
|
||||||
|
opts.read([filename])
|
||||||
|
for section, options in settings.items():
|
||||||
|
if options is None:
|
||||||
|
log.info("Deleting section [%s] from %s", section, filename)
|
||||||
|
opts.remove_section(section)
|
||||||
|
else:
|
||||||
|
if not opts.has_section(section):
|
||||||
|
log.debug("Adding new section [%s] to %s", section, filename)
|
||||||
|
opts.add_section(section)
|
||||||
|
for option,value in options.items():
|
||||||
|
if value is None:
|
||||||
|
log.debug("Deleting %s.%s from %s",
|
||||||
|
section, option, filename
|
||||||
|
)
|
||||||
|
opts.remove_option(section,option)
|
||||||
|
if not opts.options(section):
|
||||||
|
log.info("Deleting empty [%s] section from %s",
|
||||||
|
section, filename)
|
||||||
|
opts.remove_section(section)
|
||||||
|
else:
|
||||||
|
log.debug(
|
||||||
|
"Setting %s.%s to %r in %s",
|
||||||
|
section, option, value, filename
|
||||||
|
)
|
||||||
|
opts.set(section,option,value)
|
||||||
|
|
||||||
|
log.info("Writing %s", filename)
|
||||||
|
if not dry_run:
|
||||||
|
f = open(filename,'w'); opts.write(f); f.close()
|
||||||
|
|
||||||
|
class option_base(Command):
|
||||||
|
"""Abstract base class for commands that mess with config files"""
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('global-config', 'g',
|
||||||
|
"save options to the site-wide distutils.cfg file"),
|
||||||
|
('user-config', 'u',
|
||||||
|
"save options to the current user's pydistutils.cfg file"),
|
||||||
|
('filename=', 'f',
|
||||||
|
"configuration file to use (default=setup.cfg)"),
|
||||||
|
]
|
||||||
|
|
||||||
|
boolean_options = [
|
||||||
|
'global-config', 'user-config',
|
||||||
|
]
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
self.global_config = None
|
||||||
|
self.user_config = None
|
||||||
|
self.filename = None
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
filenames = []
|
||||||
|
if self.global_config:
|
||||||
|
filenames.append(config_file('global'))
|
||||||
|
if self.user_config:
|
||||||
|
filenames.append(config_file('user'))
|
||||||
|
if self.filename is not None:
|
||||||
|
filenames.append(self.filename)
|
||||||
|
if not filenames:
|
||||||
|
filenames.append(config_file('local'))
|
||||||
|
if len(filenames)>1:
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"Must specify only one configuration file option",
|
||||||
|
filenames
|
||||||
|
)
|
||||||
|
self.filename, = filenames
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class setopt(option_base):
|
||||||
|
"""Save command-line options to a file"""
|
||||||
|
|
||||||
|
description = "set an option in setup.cfg or another config file"
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('command=', 'c', 'command to set an option for'),
|
||||||
|
('option=', 'o', 'option to set'),
|
||||||
|
('set-value=', 's', 'value of the option'),
|
||||||
|
('remove', 'r', 'remove (unset) the value'),
|
||||||
|
] + option_base.user_options
|
||||||
|
|
||||||
|
boolean_options = option_base.boolean_options + ['remove']
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
option_base.initialize_options(self)
|
||||||
|
self.command = None
|
||||||
|
self.option = None
|
||||||
|
self.set_value = None
|
||||||
|
self.remove = None
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
option_base.finalize_options(self)
|
||||||
|
if self.command is None or self.option is None:
|
||||||
|
raise DistutilsOptionError("Must specify --command *and* --option")
|
||||||
|
if self.set_value is None and not self.remove:
|
||||||
|
raise DistutilsOptionError("Must specify --set-value or --remove")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
edit_config(
|
||||||
|
self.filename, {
|
||||||
|
self.command: {self.option.replace('-','_'):self.set_value}
|
||||||
|
},
|
||||||
|
self.dry_run
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+198
@@ -0,0 +1,198 @@
|
|||||||
|
from setuptools import Command
|
||||||
|
from distutils.errors import DistutilsOptionError
|
||||||
|
import sys
|
||||||
|
from pkg_resources import *
|
||||||
|
from pkg_resources import _namespace_packages
|
||||||
|
from unittest import TestLoader, main
|
||||||
|
|
||||||
|
class ScanningLoader(TestLoader):
|
||||||
|
|
||||||
|
def loadTestsFromModule(self, module):
|
||||||
|
"""Return a suite of all tests cases contained in the given module
|
||||||
|
|
||||||
|
If the module is a package, load tests from all the modules in it.
|
||||||
|
If the module has an ``additional_tests`` function, call it and add
|
||||||
|
the return value to the tests.
|
||||||
|
"""
|
||||||
|
tests = []
|
||||||
|
if module.__name__!='setuptools.tests.doctest': # ugh
|
||||||
|
tests.append(TestLoader.loadTestsFromModule(self,module))
|
||||||
|
|
||||||
|
if hasattr(module, "additional_tests"):
|
||||||
|
tests.append(module.additional_tests())
|
||||||
|
|
||||||
|
if hasattr(module, '__path__'):
|
||||||
|
for file in resource_listdir(module.__name__, ''):
|
||||||
|
if file.endswith('.py') and file!='__init__.py':
|
||||||
|
submodule = module.__name__+'.'+file[:-3]
|
||||||
|
else:
|
||||||
|
if resource_exists(
|
||||||
|
module.__name__, file+'/__init__.py'
|
||||||
|
):
|
||||||
|
submodule = module.__name__+'.'+file
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
tests.append(self.loadTestsFromName(submodule))
|
||||||
|
|
||||||
|
if len(tests)!=1:
|
||||||
|
return self.suiteClass(tests)
|
||||||
|
else:
|
||||||
|
return tests[0] # don't create a nested suite for only one return
|
||||||
|
|
||||||
|
|
||||||
|
class test(Command):
|
||||||
|
|
||||||
|
"""Command to run unit tests after in-place build"""
|
||||||
|
|
||||||
|
description = "run unit tests after in-place build"
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('test-module=','m', "Run 'test_suite' in specified module"),
|
||||||
|
('test-suite=','s',
|
||||||
|
"Test suite to run (e.g. 'some_module.test_suite')"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
self.test_suite = None
|
||||||
|
self.test_module = None
|
||||||
|
self.test_loader = None
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
|
||||||
|
if self.test_suite is None:
|
||||||
|
if self.test_module is None:
|
||||||
|
self.test_suite = self.distribution.test_suite
|
||||||
|
else:
|
||||||
|
self.test_suite = self.test_module+".test_suite"
|
||||||
|
elif self.test_module:
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"You may specify a module or a suite, but not both"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.test_args = [self.test_suite]
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
self.test_args.insert(0,'--verbose')
|
||||||
|
if self.test_loader is None:
|
||||||
|
self.test_loader = getattr(self.distribution,'test_loader',None)
|
||||||
|
if self.test_loader is None:
|
||||||
|
self.test_loader = "setuptools.command.test:ScanningLoader"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def with_project_on_sys_path(self, func):
|
||||||
|
if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
|
||||||
|
# If we run 2to3 we can not do this inplace:
|
||||||
|
|
||||||
|
# Ensure metadata is up-to-date
|
||||||
|
self.reinitialize_command('build_py', inplace=0)
|
||||||
|
self.run_command('build_py')
|
||||||
|
bpy_cmd = self.get_finalized_command("build_py")
|
||||||
|
build_path = normalize_path(bpy_cmd.build_lib)
|
||||||
|
|
||||||
|
# Build extensions
|
||||||
|
self.reinitialize_command('egg_info', egg_base=build_path)
|
||||||
|
self.run_command('egg_info')
|
||||||
|
|
||||||
|
self.reinitialize_command('build_ext', inplace=0)
|
||||||
|
self.run_command('build_ext')
|
||||||
|
else:
|
||||||
|
# Without 2to3 inplace works fine:
|
||||||
|
self.run_command('egg_info')
|
||||||
|
|
||||||
|
# Build extensions in-place
|
||||||
|
self.reinitialize_command('build_ext', inplace=1)
|
||||||
|
self.run_command('build_ext')
|
||||||
|
|
||||||
|
ei_cmd = self.get_finalized_command("egg_info")
|
||||||
|
|
||||||
|
old_path = sys.path[:]
|
||||||
|
old_modules = sys.modules.copy()
|
||||||
|
|
||||||
|
try:
|
||||||
|
sys.path.insert(0, normalize_path(ei_cmd.egg_base))
|
||||||
|
working_set.__init__()
|
||||||
|
add_activation_listener(lambda dist: dist.activate())
|
||||||
|
require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
|
||||||
|
func()
|
||||||
|
finally:
|
||||||
|
sys.path[:] = old_path
|
||||||
|
sys.modules.clear()
|
||||||
|
sys.modules.update(old_modules)
|
||||||
|
working_set.__init__()
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.distribution.install_requires:
|
||||||
|
self.distribution.fetch_build_eggs(self.distribution.install_requires)
|
||||||
|
if self.distribution.tests_require:
|
||||||
|
self.distribution.fetch_build_eggs(self.distribution.tests_require)
|
||||||
|
|
||||||
|
if self.test_suite:
|
||||||
|
cmd = ' '.join(self.test_args)
|
||||||
|
if self.dry_run:
|
||||||
|
self.announce('skipping "unittest %s" (dry run)' % cmd)
|
||||||
|
else:
|
||||||
|
self.announce('running "unittest %s"' % cmd)
|
||||||
|
self.with_project_on_sys_path(self.run_tests)
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests(self):
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
# Purge modules under test from sys.modules. The test loader will
|
||||||
|
# re-import them from the build location. Required when 2to3 is used
|
||||||
|
# with namespace packages.
|
||||||
|
if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
|
||||||
|
module = self.test_args[-1].split('.')[0]
|
||||||
|
if module in _namespace_packages:
|
||||||
|
del_modules = []
|
||||||
|
if module in sys.modules:
|
||||||
|
del_modules.append(module)
|
||||||
|
module += '.'
|
||||||
|
for name in sys.modules:
|
||||||
|
if name.startswith(module):
|
||||||
|
del_modules.append(name)
|
||||||
|
map(sys.modules.__delitem__, del_modules)
|
||||||
|
|
||||||
|
loader_ep = EntryPoint.parse("x="+self.test_loader)
|
||||||
|
loader_class = loader_ep.load(require=False)
|
||||||
|
cks = loader_class()
|
||||||
|
unittest.main(
|
||||||
|
None, None, [unittest.__file__]+self.test_args,
|
||||||
|
testLoader = cks
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
"""distutils.command.upload
|
||||||
|
|
||||||
|
Implements the Distutils 'upload' subcommand (upload package to PyPI)."""
|
||||||
|
|
||||||
|
from distutils.errors import *
|
||||||
|
from distutils.core import Command
|
||||||
|
from distutils.spawn import spawn
|
||||||
|
from distutils import log
|
||||||
|
try:
|
||||||
|
from hashlib import md5
|
||||||
|
except ImportError:
|
||||||
|
from md5 import md5
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import platform
|
||||||
|
import ConfigParser
|
||||||
|
import httplib
|
||||||
|
import base64
|
||||||
|
import urlparse
|
||||||
|
import cStringIO as StringIO
|
||||||
|
|
||||||
|
class upload(Command):
|
||||||
|
|
||||||
|
description = "upload binary package to PyPI"
|
||||||
|
|
||||||
|
DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi'
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('repository=', 'r',
|
||||||
|
"url of repository [default: %s]" % DEFAULT_REPOSITORY),
|
||||||
|
('show-response', None,
|
||||||
|
'display full response text from server'),
|
||||||
|
('sign', 's',
|
||||||
|
'sign files to upload using gpg'),
|
||||||
|
('identity=', 'i', 'GPG identity used to sign files'),
|
||||||
|
]
|
||||||
|
boolean_options = ['show-response', 'sign']
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
self.username = ''
|
||||||
|
self.password = ''
|
||||||
|
self.repository = ''
|
||||||
|
self.show_response = 0
|
||||||
|
self.sign = False
|
||||||
|
self.identity = None
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
if self.identity and not self.sign:
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"Must use --sign for --identity to have meaning"
|
||||||
|
)
|
||||||
|
if os.environ.has_key('HOME'):
|
||||||
|
rc = os.path.join(os.environ['HOME'], '.pypirc')
|
||||||
|
if os.path.exists(rc):
|
||||||
|
self.announce('Using PyPI login from %s' % rc)
|
||||||
|
config = ConfigParser.ConfigParser({
|
||||||
|
'username':'',
|
||||||
|
'password':'',
|
||||||
|
'repository':''})
|
||||||
|
config.read(rc)
|
||||||
|
if not self.repository:
|
||||||
|
self.repository = config.get('server-login', 'repository')
|
||||||
|
if not self.username:
|
||||||
|
self.username = config.get('server-login', 'username')
|
||||||
|
if not self.password:
|
||||||
|
self.password = config.get('server-login', 'password')
|
||||||
|
if not self.repository:
|
||||||
|
self.repository = self.DEFAULT_REPOSITORY
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if not self.distribution.dist_files:
|
||||||
|
raise DistutilsOptionError("No dist file created in earlier command")
|
||||||
|
for command, pyversion, filename in self.distribution.dist_files:
|
||||||
|
self.upload_file(command, pyversion, filename)
|
||||||
|
|
||||||
|
def upload_file(self, command, pyversion, filename):
|
||||||
|
# Sign if requested
|
||||||
|
if self.sign:
|
||||||
|
gpg_args = ["gpg", "--detach-sign", "-a", filename]
|
||||||
|
if self.identity:
|
||||||
|
gpg_args[2:2] = ["--local-user", self.identity]
|
||||||
|
spawn(gpg_args,
|
||||||
|
dry_run=self.dry_run)
|
||||||
|
|
||||||
|
# Fill in the data
|
||||||
|
f = open(filename,'rb')
|
||||||
|
content = f.read()
|
||||||
|
f.close()
|
||||||
|
basename = os.path.basename(filename)
|
||||||
|
comment = ''
|
||||||
|
if command=='bdist_egg' and self.distribution.has_ext_modules():
|
||||||
|
comment = "built on %s" % platform.platform(terse=1)
|
||||||
|
data = {
|
||||||
|
':action':'file_upload',
|
||||||
|
'protocol_version':'1',
|
||||||
|
'name':self.distribution.get_name(),
|
||||||
|
'version':self.distribution.get_version(),
|
||||||
|
'content':(basename,content),
|
||||||
|
'filetype':command,
|
||||||
|
'pyversion':pyversion,
|
||||||
|
'md5_digest':md5(content).hexdigest(),
|
||||||
|
}
|
||||||
|
if command == 'bdist_rpm':
|
||||||
|
dist, version, id = platform.dist()
|
||||||
|
if dist:
|
||||||
|
comment = 'built for %s %s' % (dist, version)
|
||||||
|
elif command == 'bdist_dumb':
|
||||||
|
comment = 'built for %s' % platform.platform(terse=1)
|
||||||
|
data['comment'] = comment
|
||||||
|
|
||||||
|
if self.sign:
|
||||||
|
asc_file = open(filename + ".asc")
|
||||||
|
data['gpg_signature'] = (os.path.basename(filename) + ".asc", asc_file.read())
|
||||||
|
asc_file.close()
|
||||||
|
|
||||||
|
# set up the authentication
|
||||||
|
auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip()
|
||||||
|
|
||||||
|
# Build up the MIME payload for the POST data
|
||||||
|
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
||||||
|
sep_boundary = '\n--' + boundary
|
||||||
|
end_boundary = sep_boundary + '--'
|
||||||
|
body = StringIO.StringIO()
|
||||||
|
for key, value in data.items():
|
||||||
|
# handle multiple entries for the same name
|
||||||
|
if type(value) != type([]):
|
||||||
|
value = [value]
|
||||||
|
for value in value:
|
||||||
|
if type(value) is tuple:
|
||||||
|
fn = ';filename="%s"' % value[0]
|
||||||
|
value = value[1]
|
||||||
|
else:
|
||||||
|
fn = ""
|
||||||
|
value = str(value)
|
||||||
|
body.write(sep_boundary)
|
||||||
|
body.write('\nContent-Disposition: form-data; name="%s"'%key)
|
||||||
|
body.write(fn)
|
||||||
|
body.write("\n\n")
|
||||||
|
body.write(value)
|
||||||
|
if value and value[-1] == '\r':
|
||||||
|
body.write('\n') # write an extra newline (lurve Macs)
|
||||||
|
body.write(end_boundary)
|
||||||
|
body.write("\n")
|
||||||
|
body = body.getvalue()
|
||||||
|
|
||||||
|
self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO)
|
||||||
|
|
||||||
|
# build the Request
|
||||||
|
# We can't use urllib2 since we need to send the Basic
|
||||||
|
# auth right with the first request
|
||||||
|
schema, netloc, url, params, query, fragments = \
|
||||||
|
urlparse.urlparse(self.repository)
|
||||||
|
assert not params and not query and not fragments
|
||||||
|
if schema == 'http':
|
||||||
|
http = httplib.HTTPConnection(netloc)
|
||||||
|
elif schema == 'https':
|
||||||
|
http = httplib.HTTPSConnection(netloc)
|
||||||
|
else:
|
||||||
|
raise AssertionError, "unsupported schema "+schema
|
||||||
|
|
||||||
|
data = ''
|
||||||
|
loglevel = log.INFO
|
||||||
|
try:
|
||||||
|
http.connect()
|
||||||
|
http.putrequest("POST", url)
|
||||||
|
http.putheader('Content-type',
|
||||||
|
'multipart/form-data; boundary=%s'%boundary)
|
||||||
|
http.putheader('Content-length', str(len(body)))
|
||||||
|
http.putheader('Authorization', auth)
|
||||||
|
http.endheaders()
|
||||||
|
http.send(body)
|
||||||
|
except socket.error, e:
|
||||||
|
self.announce(str(e), log.ERROR)
|
||||||
|
return
|
||||||
|
|
||||||
|
r = http.getresponse()
|
||||||
|
if r.status == 200:
|
||||||
|
self.announce('Server response (%s): %s' % (r.status, r.reason),
|
||||||
|
log.INFO)
|
||||||
|
else:
|
||||||
|
self.announce('Upload failed (%s): %s' % (r.status, r.reason),
|
||||||
|
log.ERROR)
|
||||||
|
if self.show_response:
|
||||||
|
print '-'*75, r.read(), '-'*75
|
||||||
|
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""upload_docs
|
||||||
|
|
||||||
|
Implements a Distutils 'upload_docs' subcommand (upload documentation to
|
||||||
|
PyPI's packages.python.org).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import zipfile
|
||||||
|
import httplib
|
||||||
|
import urlparse
|
||||||
|
import tempfile
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from base64 import standard_b64encode
|
||||||
|
from pkg_resources import iter_entry_points
|
||||||
|
|
||||||
|
from distutils import log
|
||||||
|
from distutils.errors import DistutilsOptionError
|
||||||
|
|
||||||
|
try:
|
||||||
|
from distutils.command.upload import upload
|
||||||
|
except ImportError:
|
||||||
|
from setuptools.command.upload import upload
|
||||||
|
|
||||||
|
|
||||||
|
# This is not just a replacement for byte literals
|
||||||
|
# but works as a general purpose encoder
|
||||||
|
def b(s, encoding='utf-8'):
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
return s.encode(encoding)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
class upload_docs(upload):
|
||||||
|
|
||||||
|
description = 'Upload documentation to PyPI'
|
||||||
|
|
||||||
|
user_options = [
|
||||||
|
('repository=', 'r',
|
||||||
|
"url of repository [default: %s]" % upload.DEFAULT_REPOSITORY),
|
||||||
|
('show-response', None,
|
||||||
|
'display full response text from server'),
|
||||||
|
('upload-dir=', None, 'directory to upload'),
|
||||||
|
]
|
||||||
|
boolean_options = upload.boolean_options
|
||||||
|
|
||||||
|
def has_sphinx(self):
|
||||||
|
if self.upload_dir is None:
|
||||||
|
for ep in iter_entry_points('distutils.commands', 'build_sphinx'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
sub_commands = [('build_sphinx', has_sphinx)]
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
upload.initialize_options(self)
|
||||||
|
self.upload_dir = None
|
||||||
|
self.target_dir = None
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
upload.finalize_options(self)
|
||||||
|
if self.upload_dir is None:
|
||||||
|
if self.has_sphinx():
|
||||||
|
build_sphinx = self.get_finalized_command('build_sphinx')
|
||||||
|
self.target_dir = build_sphinx.builder_target_dir
|
||||||
|
else:
|
||||||
|
build = self.get_finalized_command('build')
|
||||||
|
self.target_dir = os.path.join(build.build_base, 'docs')
|
||||||
|
else:
|
||||||
|
self.ensure_dirname('upload_dir')
|
||||||
|
self.target_dir = self.upload_dir
|
||||||
|
self.announce('Using upload directory %s' % self.target_dir)
|
||||||
|
|
||||||
|
def create_zipfile(self, filename):
|
||||||
|
zip_file = zipfile.ZipFile(filename, "w")
|
||||||
|
try:
|
||||||
|
self.mkpath(self.target_dir) # just in case
|
||||||
|
for root, dirs, files in os.walk(self.target_dir):
|
||||||
|
if root == self.target_dir and not files:
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"no files found in upload directory '%s'"
|
||||||
|
% self.target_dir)
|
||||||
|
for name in files:
|
||||||
|
full = os.path.join(root, name)
|
||||||
|
relative = root[len(self.target_dir):].lstrip(os.path.sep)
|
||||||
|
dest = os.path.join(relative, name)
|
||||||
|
zip_file.write(full, dest)
|
||||||
|
finally:
|
||||||
|
zip_file.close()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# Run sub commands
|
||||||
|
for cmd_name in self.get_sub_commands():
|
||||||
|
self.run_command(cmd_name)
|
||||||
|
|
||||||
|
tmp_dir = tempfile.mkdtemp()
|
||||||
|
name = self.distribution.metadata.get_name()
|
||||||
|
zip_file = os.path.join(tmp_dir, "%s.zip" % name)
|
||||||
|
try:
|
||||||
|
self.create_zipfile(zip_file)
|
||||||
|
self.upload_file(zip_file)
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(tmp_dir)
|
||||||
|
|
||||||
|
def upload_file(self, filename):
|
||||||
|
f = open(filename, 'rb')
|
||||||
|
content = f.read()
|
||||||
|
f.close()
|
||||||
|
meta = self.distribution.metadata
|
||||||
|
data = {
|
||||||
|
':action': 'doc_upload',
|
||||||
|
'name': meta.get_name(),
|
||||||
|
'content': (os.path.basename(filename), content),
|
||||||
|
}
|
||||||
|
# set up the authentication
|
||||||
|
credentials = b(self.username + ':' + self.password)
|
||||||
|
credentials = standard_b64encode(credentials)
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
credentials = credentials.decode('ascii')
|
||||||
|
auth = "Basic " + credentials
|
||||||
|
|
||||||
|
# Build up the MIME payload for the POST data
|
||||||
|
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
||||||
|
sep_boundary = b('\n--') + b(boundary)
|
||||||
|
end_boundary = sep_boundary + b('--')
|
||||||
|
body = []
|
||||||
|
for key, values in data.iteritems():
|
||||||
|
title = '\nContent-Disposition: form-data; name="%s"' % key
|
||||||
|
# handle multiple entries for the same name
|
||||||
|
if type(values) != type([]):
|
||||||
|
values = [values]
|
||||||
|
for value in values:
|
||||||
|
if type(value) is tuple:
|
||||||
|
title += '; filename="%s"' % value[0]
|
||||||
|
value = value[1]
|
||||||
|
else:
|
||||||
|
value = b(value)
|
||||||
|
body.append(sep_boundary)
|
||||||
|
body.append(b(title))
|
||||||
|
body.append(b("\n\n"))
|
||||||
|
body.append(value)
|
||||||
|
if value and value[-1:] == b('\r'):
|
||||||
|
body.append(b('\n')) # write an extra newline (lurve Macs)
|
||||||
|
body.append(end_boundary)
|
||||||
|
body.append(b("\n"))
|
||||||
|
body = b('').join(body)
|
||||||
|
|
||||||
|
self.announce("Submitting documentation to %s" % (self.repository),
|
||||||
|
log.INFO)
|
||||||
|
|
||||||
|
# build the Request
|
||||||
|
# We can't use urllib2 since we need to send the Basic
|
||||||
|
# auth right with the first request
|
||||||
|
schema, netloc, url, params, query, fragments = \
|
||||||
|
urlparse.urlparse(self.repository)
|
||||||
|
assert not params and not query and not fragments
|
||||||
|
if schema == 'http':
|
||||||
|
conn = httplib.HTTPConnection(netloc)
|
||||||
|
elif schema == 'https':
|
||||||
|
conn = httplib.HTTPSConnection(netloc)
|
||||||
|
else:
|
||||||
|
raise AssertionError("unsupported schema "+schema)
|
||||||
|
|
||||||
|
data = ''
|
||||||
|
loglevel = log.INFO
|
||||||
|
try:
|
||||||
|
conn.connect()
|
||||||
|
conn.putrequest("POST", url)
|
||||||
|
conn.putheader('Content-type',
|
||||||
|
'multipart/form-data; boundary=%s'%boundary)
|
||||||
|
conn.putheader('Content-length', str(len(body)))
|
||||||
|
conn.putheader('Authorization', auth)
|
||||||
|
conn.endheaders()
|
||||||
|
conn.send(body)
|
||||||
|
except socket.error, e:
|
||||||
|
self.announce(str(e), log.ERROR)
|
||||||
|
return
|
||||||
|
|
||||||
|
r = conn.getresponse()
|
||||||
|
if r.status == 200:
|
||||||
|
self.announce('Server response (%s): %s' % (r.status, r.reason),
|
||||||
|
log.INFO)
|
||||||
|
elif r.status == 301:
|
||||||
|
location = r.getheader('Location')
|
||||||
|
if location is None:
|
||||||
|
location = 'http://packages.python.org/%s/' % meta.get_name()
|
||||||
|
self.announce('Upload successful. Visit %s' % location,
|
||||||
|
log.INFO)
|
||||||
|
else:
|
||||||
|
self.announce('Upload failed (%s): %s' % (r.status, r.reason),
|
||||||
|
log.ERROR)
|
||||||
|
if self.show_response:
|
||||||
|
print '-'*75, r.read(), '-'*75
|
||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
from __future__ import generators
|
||||||
|
import sys, imp, marshal
|
||||||
|
from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN
|
||||||
|
from distutils.version import StrictVersion, LooseVersion
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'Require', 'find_module', 'get_module_constant', 'extract_constant'
|
||||||
|
]
|
||||||
|
|
||||||
|
class Require:
|
||||||
|
"""A prerequisite to building or installing a distribution"""
|
||||||
|
|
||||||
|
def __init__(self,name,requested_version,module,homepage='',
|
||||||
|
attribute=None,format=None
|
||||||
|
):
|
||||||
|
|
||||||
|
if format is None and requested_version is not None:
|
||||||
|
format = StrictVersion
|
||||||
|
|
||||||
|
if format is not None:
|
||||||
|
requested_version = format(requested_version)
|
||||||
|
if attribute is None:
|
||||||
|
attribute = '__version__'
|
||||||
|
|
||||||
|
self.__dict__.update(locals())
|
||||||
|
del self.self
|
||||||
|
|
||||||
|
|
||||||
|
def full_name(self):
|
||||||
|
"""Return full package/distribution name, w/version"""
|
||||||
|
if self.requested_version is not None:
|
||||||
|
return '%s-%s' % (self.name,self.requested_version)
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
def version_ok(self,version):
|
||||||
|
"""Is 'version' sufficiently up-to-date?"""
|
||||||
|
return self.attribute is None or self.format is None or \
|
||||||
|
str(version)<>"unknown" and version >= self.requested_version
|
||||||
|
|
||||||
|
|
||||||
|
def get_version(self, paths=None, default="unknown"):
|
||||||
|
|
||||||
|
"""Get version number of installed module, 'None', or 'default'
|
||||||
|
|
||||||
|
Search 'paths' for module. If not found, return 'None'. If found,
|
||||||
|
return the extracted version attribute, or 'default' if no version
|
||||||
|
attribute was specified, or the value cannot be determined without
|
||||||
|
importing the module. The version is formatted according to the
|
||||||
|
requirement's version format (if any), unless it is 'None' or the
|
||||||
|
supplied 'default'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.attribute is None:
|
||||||
|
try:
|
||||||
|
f,p,i = find_module(self.module,paths)
|
||||||
|
if f: f.close()
|
||||||
|
return default
|
||||||
|
except ImportError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
v = get_module_constant(self.module,self.attribute,default,paths)
|
||||||
|
|
||||||
|
if v is not None and v is not default and self.format is not None:
|
||||||
|
return self.format(v)
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def is_present(self,paths=None):
|
||||||
|
"""Return true if dependency is present on 'paths'"""
|
||||||
|
return self.get_version(paths) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def is_current(self,paths=None):
|
||||||
|
"""Return true if dependency is present and up-to-date on 'paths'"""
|
||||||
|
version = self.get_version(paths)
|
||||||
|
if version is None:
|
||||||
|
return False
|
||||||
|
return self.version_ok(version)
|
||||||
|
|
||||||
|
|
||||||
|
def _iter_code(code):
|
||||||
|
|
||||||
|
"""Yield '(op,arg)' pair for each operation in code object 'code'"""
|
||||||
|
|
||||||
|
from array import array
|
||||||
|
from dis import HAVE_ARGUMENT, EXTENDED_ARG
|
||||||
|
|
||||||
|
bytes = array('b',code.co_code)
|
||||||
|
eof = len(code.co_code)
|
||||||
|
|
||||||
|
ptr = 0
|
||||||
|
extended_arg = 0
|
||||||
|
|
||||||
|
while ptr<eof:
|
||||||
|
|
||||||
|
op = bytes[ptr]
|
||||||
|
|
||||||
|
if op>=HAVE_ARGUMENT:
|
||||||
|
|
||||||
|
arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg
|
||||||
|
ptr += 3
|
||||||
|
|
||||||
|
if op==EXTENDED_ARG:
|
||||||
|
extended_arg = arg * 65536L
|
||||||
|
continue
|
||||||
|
|
||||||
|
else:
|
||||||
|
arg = None
|
||||||
|
ptr += 1
|
||||||
|
|
||||||
|
yield op,arg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def find_module(module, paths=None):
|
||||||
|
"""Just like 'imp.find_module()', but with package support"""
|
||||||
|
|
||||||
|
parts = module.split('.')
|
||||||
|
|
||||||
|
while parts:
|
||||||
|
part = parts.pop(0)
|
||||||
|
f, path, (suffix,mode,kind) = info = imp.find_module(part, paths)
|
||||||
|
|
||||||
|
if kind==PKG_DIRECTORY:
|
||||||
|
parts = parts or ['__init__']
|
||||||
|
paths = [path]
|
||||||
|
|
||||||
|
elif parts:
|
||||||
|
raise ImportError("Can't find %r in %s" % (parts,module))
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_module_constant(module, symbol, default=-1, paths=None):
|
||||||
|
|
||||||
|
"""Find 'module' by searching 'paths', and extract 'symbol'
|
||||||
|
|
||||||
|
Return 'None' if 'module' does not exist on 'paths', or it does not define
|
||||||
|
'symbol'. If the module defines 'symbol' as a constant, return the
|
||||||
|
constant. Otherwise, return 'default'."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
f, path, (suffix,mode,kind) = find_module(module,paths)
|
||||||
|
except ImportError:
|
||||||
|
# Module doesn't exist
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
if kind==PY_COMPILED:
|
||||||
|
f.read(8) # skip magic & date
|
||||||
|
code = marshal.load(f)
|
||||||
|
elif kind==PY_FROZEN:
|
||||||
|
code = imp.get_frozen_object(module)
|
||||||
|
elif kind==PY_SOURCE:
|
||||||
|
code = compile(f.read(), path, 'exec')
|
||||||
|
else:
|
||||||
|
# Not something we can parse; we'll have to import it. :(
|
||||||
|
if module not in sys.modules:
|
||||||
|
imp.load_module(module,f,path,(suffix,mode,kind))
|
||||||
|
return getattr(sys.modules[module],symbol,None)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if f:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
return extract_constant(code,symbol,default)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extract_constant(code,symbol,default=-1):
|
||||||
|
"""Extract the constant value of 'symbol' from 'code'
|
||||||
|
|
||||||
|
If the name 'symbol' is bound to a constant value by the Python code
|
||||||
|
object 'code', return that value. If 'symbol' is bound to an expression,
|
||||||
|
return 'default'. Otherwise, return 'None'.
|
||||||
|
|
||||||
|
Return value is based on the first assignment to 'symbol'. 'symbol' must
|
||||||
|
be a global, or at least a non-"fast" local in the code block. That is,
|
||||||
|
only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
|
||||||
|
must be present in 'code.co_names'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if symbol not in code.co_names:
|
||||||
|
# name's not there, can't possibly be an assigment
|
||||||
|
return None
|
||||||
|
|
||||||
|
name_idx = list(code.co_names).index(symbol)
|
||||||
|
|
||||||
|
STORE_NAME = 90
|
||||||
|
STORE_GLOBAL = 97
|
||||||
|
LOAD_CONST = 100
|
||||||
|
|
||||||
|
const = default
|
||||||
|
|
||||||
|
for op, arg in _iter_code(code):
|
||||||
|
|
||||||
|
if op==LOAD_CONST:
|
||||||
|
const = code.co_consts[arg]
|
||||||
|
elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL):
|
||||||
|
return const
|
||||||
|
else:
|
||||||
|
const = default
|
||||||
|
|
||||||
|
if sys.platform.startswith('java') or sys.platform == 'cli':
|
||||||
|
# XXX it'd be better to test assertions about bytecode instead...
|
||||||
|
del extract_constant, get_module_constant
|
||||||
|
__all__.remove('extract_constant')
|
||||||
|
__all__.remove('get_module_constant')
|
||||||
|
|
||||||
|
|
||||||
+855
@@ -0,0 +1,855 @@
|
|||||||
|
__all__ = ['Distribution']
|
||||||
|
|
||||||
|
import re
|
||||||
|
from distutils.core import Distribution as _Distribution
|
||||||
|
from setuptools.depends import Require
|
||||||
|
from setuptools.command.install import install
|
||||||
|
from setuptools.command.sdist import sdist
|
||||||
|
from setuptools.command.install_lib import install_lib
|
||||||
|
from distutils.errors import DistutilsOptionError, DistutilsPlatformError
|
||||||
|
from distutils.errors import DistutilsSetupError
|
||||||
|
import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd
|
||||||
|
import os, distutils.log
|
||||||
|
|
||||||
|
def _get_unpatched(cls):
|
||||||
|
"""Protect against re-patching the distutils if reloaded
|
||||||
|
|
||||||
|
Also ensures that no other distutils extension monkeypatched the distutils
|
||||||
|
first.
|
||||||
|
"""
|
||||||
|
while cls.__module__.startswith('setuptools'):
|
||||||
|
cls, = cls.__bases__
|
||||||
|
if not cls.__module__.startswith('distutils'):
|
||||||
|
raise AssertionError(
|
||||||
|
"distutils has already been patched by %r" % cls
|
||||||
|
)
|
||||||
|
return cls
|
||||||
|
|
||||||
|
_Distribution = _get_unpatched(_Distribution)
|
||||||
|
|
||||||
|
sequence = tuple, list
|
||||||
|
|
||||||
|
def check_importable(dist, attr, value):
|
||||||
|
try:
|
||||||
|
ep = pkg_resources.EntryPoint.parse('x='+value)
|
||||||
|
assert not ep.extras
|
||||||
|
except (TypeError,ValueError,AttributeError,AssertionError):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%r must be importable 'module:attrs' string (got %r)"
|
||||||
|
% (attr,value)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def assert_string_list(dist, attr, value):
|
||||||
|
"""Verify that value is a string list or None"""
|
||||||
|
try:
|
||||||
|
assert ''.join(value)!=value
|
||||||
|
except (TypeError,ValueError,AttributeError,AssertionError):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%r must be a list of strings (got %r)" % (attr,value)
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_nsp(dist, attr, value):
|
||||||
|
"""Verify that namespace packages are valid"""
|
||||||
|
assert_string_list(dist,attr,value)
|
||||||
|
for nsp in value:
|
||||||
|
if not dist.has_contents_for(nsp):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"Distribution contains no modules or packages for " +
|
||||||
|
"namespace package %r" % nsp
|
||||||
|
)
|
||||||
|
if '.' in nsp:
|
||||||
|
parent = '.'.join(nsp.split('.')[:-1])
|
||||||
|
if parent not in value:
|
||||||
|
distutils.log.warn(
|
||||||
|
"%r is declared as a package namespace, but %r is not:"
|
||||||
|
" please correct this in setup.py", nsp, parent
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_extras(dist, attr, value):
|
||||||
|
"""Verify that extras_require mapping is valid"""
|
||||||
|
try:
|
||||||
|
for k,v in value.items():
|
||||||
|
list(pkg_resources.parse_requirements(v))
|
||||||
|
except (TypeError,ValueError,AttributeError):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"'extras_require' must be a dictionary whose values are "
|
||||||
|
"strings or lists of strings containing valid project/version "
|
||||||
|
"requirement specifiers."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def assert_bool(dist, attr, value):
|
||||||
|
"""Verify that value is True, False, 0, or 1"""
|
||||||
|
if bool(value) != value:
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%r must be a boolean value (got %r)" % (attr,value)
|
||||||
|
)
|
||||||
|
def check_requirements(dist, attr, value):
|
||||||
|
"""Verify that install_requires is a valid requirements list"""
|
||||||
|
try:
|
||||||
|
list(pkg_resources.parse_requirements(value))
|
||||||
|
except (TypeError,ValueError):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%r must be a string or list of strings "
|
||||||
|
"containing valid project/version requirement specifiers" % (attr,)
|
||||||
|
)
|
||||||
|
def check_entry_points(dist, attr, value):
|
||||||
|
"""Verify that entry_points map is parseable"""
|
||||||
|
try:
|
||||||
|
pkg_resources.EntryPoint.parse_map(value)
|
||||||
|
except ValueError, e:
|
||||||
|
raise DistutilsSetupError(e)
|
||||||
|
|
||||||
|
def check_test_suite(dist, attr, value):
|
||||||
|
if not isinstance(value,basestring):
|
||||||
|
raise DistutilsSetupError("test_suite must be a string")
|
||||||
|
|
||||||
|
def check_package_data(dist, attr, value):
|
||||||
|
"""Verify that value is a dictionary of package names to glob lists"""
|
||||||
|
if isinstance(value,dict):
|
||||||
|
for k,v in value.items():
|
||||||
|
if not isinstance(k,str): break
|
||||||
|
try: iter(v)
|
||||||
|
except TypeError:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
attr+" must be a dictionary mapping package names to lists of "
|
||||||
|
"wildcard patterns"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Distribution(_Distribution):
|
||||||
|
"""Distribution with support for features, tests, and package data
|
||||||
|
|
||||||
|
This is an enhanced version of 'distutils.dist.Distribution' that
|
||||||
|
effectively adds the following new optional keyword arguments to 'setup()':
|
||||||
|
|
||||||
|
'install_requires' -- a string or sequence of strings specifying project
|
||||||
|
versions that the distribution requires when installed, in the format
|
||||||
|
used by 'pkg_resources.require()'. They will be installed
|
||||||
|
automatically when the package is installed. If you wish to use
|
||||||
|
packages that are not available in PyPI, or want to give your users an
|
||||||
|
alternate download location, you can add a 'find_links' option to the
|
||||||
|
'[easy_install]' section of your project's 'setup.cfg' file, and then
|
||||||
|
setuptools will scan the listed web pages for links that satisfy the
|
||||||
|
requirements.
|
||||||
|
|
||||||
|
'extras_require' -- a dictionary mapping names of optional "extras" to the
|
||||||
|
additional requirement(s) that using those extras incurs. For example,
|
||||||
|
this::
|
||||||
|
|
||||||
|
extras_require = dict(reST = ["docutils>=0.3", "reSTedit"])
|
||||||
|
|
||||||
|
indicates that the distribution can optionally provide an extra
|
||||||
|
capability called "reST", but it can only be used if docutils and
|
||||||
|
reSTedit are installed. If the user installs your package using
|
||||||
|
EasyInstall and requests one of your extras, the corresponding
|
||||||
|
additional requirements will be installed if needed.
|
||||||
|
|
||||||
|
'features' -- a dictionary mapping option names to 'setuptools.Feature'
|
||||||
|
objects. Features are a portion of the distribution that can be
|
||||||
|
included or excluded based on user options, inter-feature dependencies,
|
||||||
|
and availability on the current system. Excluded features are omitted
|
||||||
|
from all setup commands, including source and binary distributions, so
|
||||||
|
you can create multiple distributions from the same source tree.
|
||||||
|
Feature names should be valid Python identifiers, except that they may
|
||||||
|
contain the '-' (minus) sign. Features can be included or excluded
|
||||||
|
via the command line options '--with-X' and '--without-X', where 'X' is
|
||||||
|
the name of the feature. Whether a feature is included by default, and
|
||||||
|
whether you are allowed to control this from the command line, is
|
||||||
|
determined by the Feature object. See the 'Feature' class for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
'test_suite' -- the name of a test suite to run for the 'test' command.
|
||||||
|
If the user runs 'python setup.py test', the package will be installed,
|
||||||
|
and the named test suite will be run. The format is the same as
|
||||||
|
would be used on a 'unittest.py' command line. That is, it is the
|
||||||
|
dotted name of an object to import and call to generate a test suite.
|
||||||
|
|
||||||
|
'package_data' -- a dictionary mapping package names to lists of filenames
|
||||||
|
or globs to use to find data files contained in the named packages.
|
||||||
|
If the dictionary has filenames or globs listed under '""' (the empty
|
||||||
|
string), those names will be searched for in every package, in addition
|
||||||
|
to any names for the specific package. Data files found using these
|
||||||
|
names/globs will be installed along with the package, in the same
|
||||||
|
location as the package. Note that globs are allowed to reference
|
||||||
|
the contents of non-package subdirectories, as long as you use '/' as
|
||||||
|
a path separator. (Globs are automatically converted to
|
||||||
|
platform-specific paths at runtime.)
|
||||||
|
|
||||||
|
In addition to these new keywords, this class also has several new methods
|
||||||
|
for manipulating the distribution's contents. For example, the 'include()'
|
||||||
|
and 'exclude()' methods can be thought of as in-place add and subtract
|
||||||
|
commands that add or remove packages, modules, extensions, and so on from
|
||||||
|
the distribution. They are used by the feature subsystem to configure the
|
||||||
|
distribution for the included and excluded features.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_patched_dist = None
|
||||||
|
|
||||||
|
def patch_missing_pkg_info(self, attrs):
|
||||||
|
# Fake up a replacement for the data that would normally come from
|
||||||
|
# PKG-INFO, but which might not yet be built if this is a fresh
|
||||||
|
# checkout.
|
||||||
|
#
|
||||||
|
if not attrs or 'name' not in attrs or 'version' not in attrs:
|
||||||
|
return
|
||||||
|
key = pkg_resources.safe_name(str(attrs['name'])).lower()
|
||||||
|
dist = pkg_resources.working_set.by_key.get(key)
|
||||||
|
if dist is not None and not dist.has_metadata('PKG-INFO'):
|
||||||
|
dist._version = pkg_resources.safe_version(str(attrs['version']))
|
||||||
|
self._patched_dist = dist
|
||||||
|
|
||||||
|
def __init__ (self, attrs=None):
|
||||||
|
have_package_data = hasattr(self, "package_data")
|
||||||
|
if not have_package_data:
|
||||||
|
self.package_data = {}
|
||||||
|
self.require_features = []
|
||||||
|
self.features = {}
|
||||||
|
self.dist_files = []
|
||||||
|
self.src_root = attrs and attrs.pop("src_root", None)
|
||||||
|
self.patch_missing_pkg_info(attrs)
|
||||||
|
# Make sure we have any eggs needed to interpret 'attrs'
|
||||||
|
if attrs is not None:
|
||||||
|
self.dependency_links = attrs.pop('dependency_links', [])
|
||||||
|
assert_string_list(self,'dependency_links',self.dependency_links)
|
||||||
|
if attrs and 'setup_requires' in attrs:
|
||||||
|
self.fetch_build_eggs(attrs.pop('setup_requires'))
|
||||||
|
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
|
||||||
|
if not hasattr(self,ep.name):
|
||||||
|
setattr(self,ep.name,None)
|
||||||
|
_Distribution.__init__(self,attrs)
|
||||||
|
if isinstance(self.metadata.version, (int,long,float)):
|
||||||
|
# Some people apparently take "version number" too literally :)
|
||||||
|
self.metadata.version = str(self.metadata.version)
|
||||||
|
|
||||||
|
def parse_command_line(self):
|
||||||
|
"""Process features after parsing command line options"""
|
||||||
|
result = _Distribution.parse_command_line(self)
|
||||||
|
if self.features:
|
||||||
|
self._finalize_features()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _feature_attrname(self,name):
|
||||||
|
"""Convert feature name to corresponding option attribute name"""
|
||||||
|
return 'with_'+name.replace('-','_')
|
||||||
|
|
||||||
|
def fetch_build_eggs(self, requires):
|
||||||
|
"""Resolve pre-setup requirements"""
|
||||||
|
from pkg_resources import working_set, parse_requirements
|
||||||
|
for dist in working_set.resolve(
|
||||||
|
parse_requirements(requires), installer=self.fetch_build_egg
|
||||||
|
):
|
||||||
|
working_set.add(dist)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
_Distribution.finalize_options(self)
|
||||||
|
if self.features:
|
||||||
|
self._set_global_opts_from_features()
|
||||||
|
|
||||||
|
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
|
||||||
|
value = getattr(self,ep.name,None)
|
||||||
|
if value is not None:
|
||||||
|
ep.require(installer=self.fetch_build_egg)
|
||||||
|
ep.load()(self, ep.name, value)
|
||||||
|
if getattr(self, 'convert_2to3_doctests', None):
|
||||||
|
# XXX may convert to set here when we can rely on set being builtin
|
||||||
|
self.convert_2to3_doctests = [os.path.abspath(p) for p in self.convert_2to3_doctests]
|
||||||
|
else:
|
||||||
|
self.convert_2to3_doctests = []
|
||||||
|
|
||||||
|
def fetch_build_egg(self, req):
|
||||||
|
"""Fetch an egg needed for building"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = self._egg_fetcher
|
||||||
|
cmd.package_index.to_scan = []
|
||||||
|
except AttributeError:
|
||||||
|
from setuptools.command.easy_install import easy_install
|
||||||
|
dist = self.__class__({'script_args':['easy_install']})
|
||||||
|
dist.parse_config_files()
|
||||||
|
opts = dist.get_option_dict('easy_install')
|
||||||
|
keep = (
|
||||||
|
'find_links', 'site_dirs', 'index_url', 'optimize',
|
||||||
|
'site_dirs', 'allow_hosts'
|
||||||
|
)
|
||||||
|
for key in opts.keys():
|
||||||
|
if key not in keep:
|
||||||
|
del opts[key] # don't use any other settings
|
||||||
|
if self.dependency_links:
|
||||||
|
links = self.dependency_links[:]
|
||||||
|
if 'find_links' in opts:
|
||||||
|
links = opts['find_links'][1].split() + links
|
||||||
|
opts['find_links'] = ('setup', links)
|
||||||
|
cmd = easy_install(
|
||||||
|
dist, args=["x"], install_dir=os.curdir, exclude_scripts=True,
|
||||||
|
always_copy=False, build_directory=None, editable=False,
|
||||||
|
upgrade=False, multi_version=True, no_report=True, user=False
|
||||||
|
)
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
self._egg_fetcher = cmd
|
||||||
|
return cmd.easy_install(req)
|
||||||
|
|
||||||
|
def _set_global_opts_from_features(self):
|
||||||
|
"""Add --with-X/--without-X options based on optional features"""
|
||||||
|
|
||||||
|
go = []
|
||||||
|
no = self.negative_opt.copy()
|
||||||
|
|
||||||
|
for name,feature in self.features.items():
|
||||||
|
self._set_feature(name,None)
|
||||||
|
feature.validate(self)
|
||||||
|
|
||||||
|
if feature.optional:
|
||||||
|
descr = feature.description
|
||||||
|
incdef = ' (default)'
|
||||||
|
excdef=''
|
||||||
|
if not feature.include_by_default():
|
||||||
|
excdef, incdef = incdef, excdef
|
||||||
|
|
||||||
|
go.append(('with-'+name, None, 'include '+descr+incdef))
|
||||||
|
go.append(('without-'+name, None, 'exclude '+descr+excdef))
|
||||||
|
no['without-'+name] = 'with-'+name
|
||||||
|
|
||||||
|
self.global_options = self.feature_options = go + self.global_options
|
||||||
|
self.negative_opt = self.feature_negopt = no
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _finalize_features(self):
|
||||||
|
"""Add/remove features and resolve dependencies between them"""
|
||||||
|
|
||||||
|
# First, flag all the enabled items (and thus their dependencies)
|
||||||
|
for name,feature in self.features.items():
|
||||||
|
enabled = self.feature_is_included(name)
|
||||||
|
if enabled or (enabled is None and feature.include_by_default()):
|
||||||
|
feature.include_in(self)
|
||||||
|
self._set_feature(name,1)
|
||||||
|
|
||||||
|
# Then disable the rest, so that off-by-default features don't
|
||||||
|
# get flagged as errors when they're required by an enabled feature
|
||||||
|
for name,feature in self.features.items():
|
||||||
|
if not self.feature_is_included(name):
|
||||||
|
feature.exclude_from(self)
|
||||||
|
self._set_feature(name,0)
|
||||||
|
|
||||||
|
|
||||||
|
def get_command_class(self, command):
|
||||||
|
"""Pluggable version of get_command_class()"""
|
||||||
|
if command in self.cmdclass:
|
||||||
|
return self.cmdclass[command]
|
||||||
|
|
||||||
|
for ep in pkg_resources.iter_entry_points('distutils.commands',command):
|
||||||
|
ep.require(installer=self.fetch_build_egg)
|
||||||
|
self.cmdclass[command] = cmdclass = ep.load()
|
||||||
|
return cmdclass
|
||||||
|
else:
|
||||||
|
return _Distribution.get_command_class(self, command)
|
||||||
|
|
||||||
|
def print_commands(self):
|
||||||
|
for ep in pkg_resources.iter_entry_points('distutils.commands'):
|
||||||
|
if ep.name not in self.cmdclass:
|
||||||
|
cmdclass = ep.load(False) # don't require extras, we're not running
|
||||||
|
self.cmdclass[ep.name] = cmdclass
|
||||||
|
return _Distribution.print_commands(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _set_feature(self,name,status):
|
||||||
|
"""Set feature's inclusion status"""
|
||||||
|
setattr(self,self._feature_attrname(name),status)
|
||||||
|
|
||||||
|
def feature_is_included(self,name):
|
||||||
|
"""Return 1 if feature is included, 0 if excluded, 'None' if unknown"""
|
||||||
|
return getattr(self,self._feature_attrname(name))
|
||||||
|
|
||||||
|
def include_feature(self,name):
|
||||||
|
"""Request inclusion of feature named 'name'"""
|
||||||
|
|
||||||
|
if self.feature_is_included(name)==0:
|
||||||
|
descr = self.features[name].description
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
descr + " is required, but was excluded or is not available"
|
||||||
|
)
|
||||||
|
self.features[name].include_in(self)
|
||||||
|
self._set_feature(name,1)
|
||||||
|
|
||||||
|
def include(self,**attrs):
|
||||||
|
"""Add items to distribution that are named in keyword arguments
|
||||||
|
|
||||||
|
For example, 'dist.exclude(py_modules=["x"])' would add 'x' to
|
||||||
|
the distribution's 'py_modules' attribute, if it was not already
|
||||||
|
there.
|
||||||
|
|
||||||
|
Currently, this method only supports inclusion for attributes that are
|
||||||
|
lists or tuples. If you need to add support for adding to other
|
||||||
|
attributes in this or a subclass, you can add an '_include_X' method,
|
||||||
|
where 'X' is the name of the attribute. The method will be called with
|
||||||
|
the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})'
|
||||||
|
will try to call 'dist._include_foo({"bar":"baz"})', which can then
|
||||||
|
handle whatever special inclusion logic is needed.
|
||||||
|
"""
|
||||||
|
for k,v in attrs.items():
|
||||||
|
include = getattr(self, '_include_'+k, None)
|
||||||
|
if include:
|
||||||
|
include(v)
|
||||||
|
else:
|
||||||
|
self._include_misc(k,v)
|
||||||
|
|
||||||
|
def exclude_package(self,package):
|
||||||
|
"""Remove packages, modules, and extensions in named package"""
|
||||||
|
|
||||||
|
pfx = package+'.'
|
||||||
|
if self.packages:
|
||||||
|
self.packages = [
|
||||||
|
p for p in self.packages
|
||||||
|
if p != package and not p.startswith(pfx)
|
||||||
|
]
|
||||||
|
|
||||||
|
if self.py_modules:
|
||||||
|
self.py_modules = [
|
||||||
|
p for p in self.py_modules
|
||||||
|
if p != package and not p.startswith(pfx)
|
||||||
|
]
|
||||||
|
|
||||||
|
if self.ext_modules:
|
||||||
|
self.ext_modules = [
|
||||||
|
p for p in self.ext_modules
|
||||||
|
if p.name != package and not p.name.startswith(pfx)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def has_contents_for(self,package):
|
||||||
|
"""Return true if 'exclude_package(package)' would do something"""
|
||||||
|
|
||||||
|
pfx = package+'.'
|
||||||
|
|
||||||
|
for p in self.iter_distribution_names():
|
||||||
|
if p==package or p.startswith(pfx):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _exclude_misc(self,name,value):
|
||||||
|
"""Handle 'exclude()' for list/tuple attrs without a special handler"""
|
||||||
|
if not isinstance(value,sequence):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%s: setting must be a list or tuple (%r)" % (name, value)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
old = getattr(self,name)
|
||||||
|
except AttributeError:
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%s: No such distribution setting" % name
|
||||||
|
)
|
||||||
|
if old is not None and not isinstance(old,sequence):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
name+": this setting cannot be changed via include/exclude"
|
||||||
|
)
|
||||||
|
elif old:
|
||||||
|
setattr(self,name,[item for item in old if item not in value])
|
||||||
|
|
||||||
|
def _include_misc(self,name,value):
|
||||||
|
"""Handle 'include()' for list/tuple attrs without a special handler"""
|
||||||
|
|
||||||
|
if not isinstance(value,sequence):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%s: setting must be a list (%r)" % (name, value)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
old = getattr(self,name)
|
||||||
|
except AttributeError:
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%s: No such distribution setting" % name
|
||||||
|
)
|
||||||
|
if old is None:
|
||||||
|
setattr(self,name,value)
|
||||||
|
elif not isinstance(old,sequence):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
name+": this setting cannot be changed via include/exclude"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
setattr(self,name,old+[item for item in value if item not in old])
|
||||||
|
|
||||||
|
def exclude(self,**attrs):
|
||||||
|
"""Remove items from distribution that are named in keyword arguments
|
||||||
|
|
||||||
|
For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from
|
||||||
|
the distribution's 'py_modules' attribute. Excluding packages uses
|
||||||
|
the 'exclude_package()' method, so all of the package's contained
|
||||||
|
packages, modules, and extensions are also excluded.
|
||||||
|
|
||||||
|
Currently, this method only supports exclusion from attributes that are
|
||||||
|
lists or tuples. If you need to add support for excluding from other
|
||||||
|
attributes in this or a subclass, you can add an '_exclude_X' method,
|
||||||
|
where 'X' is the name of the attribute. The method will be called with
|
||||||
|
the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})'
|
||||||
|
will try to call 'dist._exclude_foo({"bar":"baz"})', which can then
|
||||||
|
handle whatever special exclusion logic is needed.
|
||||||
|
"""
|
||||||
|
for k,v in attrs.items():
|
||||||
|
exclude = getattr(self, '_exclude_'+k, None)
|
||||||
|
if exclude:
|
||||||
|
exclude(v)
|
||||||
|
else:
|
||||||
|
self._exclude_misc(k,v)
|
||||||
|
|
||||||
|
def _exclude_packages(self,packages):
|
||||||
|
if not isinstance(packages,sequence):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"packages: setting must be a list or tuple (%r)" % (packages,)
|
||||||
|
)
|
||||||
|
map(self.exclude_package, packages)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_command_opts(self, parser, args):
|
||||||
|
# Remove --with-X/--without-X options when processing command args
|
||||||
|
self.global_options = self.__class__.global_options
|
||||||
|
self.negative_opt = self.__class__.negative_opt
|
||||||
|
|
||||||
|
# First, expand any aliases
|
||||||
|
command = args[0]
|
||||||
|
aliases = self.get_option_dict('aliases')
|
||||||
|
while command in aliases:
|
||||||
|
src,alias = aliases[command]
|
||||||
|
del aliases[command] # ensure each alias can expand only once!
|
||||||
|
import shlex
|
||||||
|
args[:1] = shlex.split(alias,True)
|
||||||
|
command = args[0]
|
||||||
|
|
||||||
|
nargs = _Distribution._parse_command_opts(self, parser, args)
|
||||||
|
|
||||||
|
# Handle commands that want to consume all remaining arguments
|
||||||
|
cmd_class = self.get_command_class(command)
|
||||||
|
if getattr(cmd_class,'command_consumes_arguments',None):
|
||||||
|
self.get_option_dict(command)['args'] = ("command line", nargs)
|
||||||
|
if nargs is not None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
return nargs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_cmdline_options(self):
|
||||||
|
"""Return a '{cmd: {opt:val}}' map of all command-line options
|
||||||
|
|
||||||
|
Option names are all long, but do not include the leading '--', and
|
||||||
|
contain dashes rather than underscores. If the option doesn't take
|
||||||
|
an argument (e.g. '--quiet'), the 'val' is 'None'.
|
||||||
|
|
||||||
|
Note that options provided by config files are intentionally excluded.
|
||||||
|
"""
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
|
||||||
|
for cmd,opts in self.command_options.items():
|
||||||
|
|
||||||
|
for opt,(src,val) in opts.items():
|
||||||
|
|
||||||
|
if src != "command line":
|
||||||
|
continue
|
||||||
|
|
||||||
|
opt = opt.replace('_','-')
|
||||||
|
|
||||||
|
if val==0:
|
||||||
|
cmdobj = self.get_command_obj(cmd)
|
||||||
|
neg_opt = self.negative_opt.copy()
|
||||||
|
neg_opt.update(getattr(cmdobj,'negative_opt',{}))
|
||||||
|
for neg,pos in neg_opt.items():
|
||||||
|
if pos==opt:
|
||||||
|
opt=neg
|
||||||
|
val=None
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise AssertionError("Shouldn't be able to get here")
|
||||||
|
|
||||||
|
elif val==1:
|
||||||
|
val = None
|
||||||
|
|
||||||
|
d.setdefault(cmd,{})[opt] = val
|
||||||
|
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def iter_distribution_names(self):
|
||||||
|
"""Yield all packages, modules, and extension names in distribution"""
|
||||||
|
|
||||||
|
for pkg in self.packages or ():
|
||||||
|
yield pkg
|
||||||
|
|
||||||
|
for module in self.py_modules or ():
|
||||||
|
yield module
|
||||||
|
|
||||||
|
for ext in self.ext_modules or ():
|
||||||
|
if isinstance(ext,tuple):
|
||||||
|
name, buildinfo = ext
|
||||||
|
else:
|
||||||
|
name = ext.name
|
||||||
|
if name.endswith('module'):
|
||||||
|
name = name[:-6]
|
||||||
|
yield name
|
||||||
|
|
||||||
|
|
||||||
|
def handle_display_options(self, option_order):
|
||||||
|
"""If there were any non-global "display-only" options
|
||||||
|
(--help-commands or the metadata display options) on the command
|
||||||
|
line, display the requested info and return true; else return
|
||||||
|
false.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info < (3,) or self.help_commands:
|
||||||
|
return _Distribution.handle_display_options(self, option_order)
|
||||||
|
|
||||||
|
# Stdout may be StringIO (e.g. in tests)
|
||||||
|
import io
|
||||||
|
if not isinstance(sys.stdout, io.TextIOWrapper):
|
||||||
|
return _Distribution.handle_display_options(self, option_order)
|
||||||
|
|
||||||
|
# Don't wrap stdout if utf-8 is already the encoding. Provides
|
||||||
|
# workaround for #334.
|
||||||
|
if sys.stdout.encoding.lower() in ('utf-8', 'utf8'):
|
||||||
|
return _Distribution.handle_display_options(self, option_order)
|
||||||
|
|
||||||
|
# Print metadata in UTF-8 no matter the platform
|
||||||
|
encoding = sys.stdout.encoding
|
||||||
|
errors = sys.stdout.errors
|
||||||
|
newline = sys.platform != 'win32' and '\n' or None
|
||||||
|
line_buffering = sys.stdout.line_buffering
|
||||||
|
|
||||||
|
sys.stdout = io.TextIOWrapper(
|
||||||
|
sys.stdout.detach(), 'utf-8', errors, newline, line_buffering)
|
||||||
|
try:
|
||||||
|
return _Distribution.handle_display_options(self, option_order)
|
||||||
|
finally:
|
||||||
|
sys.stdout = io.TextIOWrapper(
|
||||||
|
sys.stdout.detach(), encoding, errors, newline, line_buffering)
|
||||||
|
|
||||||
|
|
||||||
|
# Install it throughout the distutils
|
||||||
|
for module in distutils.dist, distutils.core, distutils.cmd:
|
||||||
|
module.Distribution = Distribution
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Feature:
|
||||||
|
"""A subset of the distribution that can be excluded if unneeded/wanted
|
||||||
|
|
||||||
|
Features are created using these keyword arguments:
|
||||||
|
|
||||||
|
'description' -- a short, human readable description of the feature, to
|
||||||
|
be used in error messages, and option help messages.
|
||||||
|
|
||||||
|
'standard' -- if true, the feature is included by default if it is
|
||||||
|
available on the current system. Otherwise, the feature is only
|
||||||
|
included if requested via a command line '--with-X' option, or if
|
||||||
|
another included feature requires it. The default setting is 'False'.
|
||||||
|
|
||||||
|
'available' -- if true, the feature is available for installation on the
|
||||||
|
current system. The default setting is 'True'.
|
||||||
|
|
||||||
|
'optional' -- if true, the feature's inclusion can be controlled from the
|
||||||
|
command line, using the '--with-X' or '--without-X' options. If
|
||||||
|
false, the feature's inclusion status is determined automatically,
|
||||||
|
based on 'availabile', 'standard', and whether any other feature
|
||||||
|
requires it. The default setting is 'True'.
|
||||||
|
|
||||||
|
'require_features' -- a string or sequence of strings naming features
|
||||||
|
that should also be included if this feature is included. Defaults to
|
||||||
|
empty list. May also contain 'Require' objects that should be
|
||||||
|
added/removed from the distribution.
|
||||||
|
|
||||||
|
'remove' -- a string or list of strings naming packages to be removed
|
||||||
|
from the distribution if this feature is *not* included. If the
|
||||||
|
feature *is* included, this argument is ignored. This argument exists
|
||||||
|
to support removing features that "crosscut" a distribution, such as
|
||||||
|
defining a 'tests' feature that removes all the 'tests' subpackages
|
||||||
|
provided by other features. The default for this argument is an empty
|
||||||
|
list. (Note: the named package(s) or modules must exist in the base
|
||||||
|
distribution when the 'setup()' function is initially called.)
|
||||||
|
|
||||||
|
other keywords -- any other keyword arguments are saved, and passed to
|
||||||
|
the distribution's 'include()' and 'exclude()' methods when the
|
||||||
|
feature is included or excluded, respectively. So, for example, you
|
||||||
|
could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be
|
||||||
|
added or removed from the distribution as appropriate.
|
||||||
|
|
||||||
|
A feature must include at least one 'requires', 'remove', or other
|
||||||
|
keyword argument. Otherwise, it can't affect the distribution in any way.
|
||||||
|
Note also that you can subclass 'Feature' to create your own specialized
|
||||||
|
feature types that modify the distribution in other ways when included or
|
||||||
|
excluded. See the docstrings for the various methods here for more detail.
|
||||||
|
Aside from the methods, the only feature attributes that distributions look
|
||||||
|
at are 'description' and 'optional'.
|
||||||
|
"""
|
||||||
|
def __init__(self, description, standard=False, available=True,
|
||||||
|
optional=True, require_features=(), remove=(), **extras
|
||||||
|
):
|
||||||
|
|
||||||
|
self.description = description
|
||||||
|
self.standard = standard
|
||||||
|
self.available = available
|
||||||
|
self.optional = optional
|
||||||
|
if isinstance(require_features,(str,Require)):
|
||||||
|
require_features = require_features,
|
||||||
|
|
||||||
|
self.require_features = [
|
||||||
|
r for r in require_features if isinstance(r,str)
|
||||||
|
]
|
||||||
|
er = [r for r in require_features if not isinstance(r,str)]
|
||||||
|
if er: extras['require_features'] = er
|
||||||
|
|
||||||
|
if isinstance(remove,str):
|
||||||
|
remove = remove,
|
||||||
|
self.remove = remove
|
||||||
|
self.extras = extras
|
||||||
|
|
||||||
|
if not remove and not require_features and not extras:
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"Feature %s: must define 'require_features', 'remove', or at least one"
|
||||||
|
" of 'packages', 'py_modules', etc."
|
||||||
|
)
|
||||||
|
|
||||||
|
def include_by_default(self):
|
||||||
|
"""Should this feature be included by default?"""
|
||||||
|
return self.available and self.standard
|
||||||
|
|
||||||
|
def include_in(self,dist):
|
||||||
|
|
||||||
|
"""Ensure feature and its requirements are included in distribution
|
||||||
|
|
||||||
|
You may override this in a subclass to perform additional operations on
|
||||||
|
the distribution. Note that this method may be called more than once
|
||||||
|
per feature, and so should be idempotent.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.available:
|
||||||
|
raise DistutilsPlatformError(
|
||||||
|
self.description+" is required,"
|
||||||
|
"but is not available on this platform"
|
||||||
|
)
|
||||||
|
|
||||||
|
dist.include(**self.extras)
|
||||||
|
|
||||||
|
for f in self.require_features:
|
||||||
|
dist.include_feature(f)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def exclude_from(self,dist):
|
||||||
|
|
||||||
|
"""Ensure feature is excluded from distribution
|
||||||
|
|
||||||
|
You may override this in a subclass to perform additional operations on
|
||||||
|
the distribution. This method will be called at most once per
|
||||||
|
feature, and only after all included features have been asked to
|
||||||
|
include themselves.
|
||||||
|
"""
|
||||||
|
|
||||||
|
dist.exclude(**self.extras)
|
||||||
|
|
||||||
|
if self.remove:
|
||||||
|
for item in self.remove:
|
||||||
|
dist.exclude_package(item)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def validate(self,dist):
|
||||||
|
|
||||||
|
"""Verify that feature makes sense in context of distribution
|
||||||
|
|
||||||
|
This method is called by the distribution just before it parses its
|
||||||
|
command line. It checks to ensure that the 'remove' attribute, if any,
|
||||||
|
contains only valid package/module names that are present in the base
|
||||||
|
distribution when 'setup()' is called. You may override it in a
|
||||||
|
subclass to perform any other required validation of the feature
|
||||||
|
against a target distribution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for item in self.remove:
|
||||||
|
if not dist.has_contents_for(item):
|
||||||
|
raise DistutilsSetupError(
|
||||||
|
"%s wants to be able to remove %s, but the distribution"
|
||||||
|
" doesn't contain any packages or modules under %s"
|
||||||
|
% (self.description, item, item)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def check_packages(dist, attr, value):
|
||||||
|
for pkgname in value:
|
||||||
|
if not re.match(r'\w+(\.\w+)*', pkgname):
|
||||||
|
distutils.log.warn(
|
||||||
|
"WARNING: %r not a valid package name; please use only"
|
||||||
|
".-separated package names in setup.py", pkgname
|
||||||
|
)
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import sys
|
||||||
|
import distutils.core
|
||||||
|
import distutils.extension
|
||||||
|
|
||||||
|
from setuptools.dist import _get_unpatched
|
||||||
|
|
||||||
|
_Extension = _get_unpatched(distutils.core.Extension)
|
||||||
|
|
||||||
|
def have_pyrex():
|
||||||
|
"""
|
||||||
|
Return True if Cython or Pyrex can be imported.
|
||||||
|
"""
|
||||||
|
pyrex_impls = 'Cython.Distutils.build_ext', 'Pyrex.Distutils.build_ext'
|
||||||
|
for pyrex_impl in pyrex_impls:
|
||||||
|
try:
|
||||||
|
# from (pyrex_impl) import build_ext
|
||||||
|
__import__(pyrex_impl, fromlist=['build_ext']).build_ext
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Extension(_Extension):
|
||||||
|
"""Extension that uses '.c' files in place of '.pyx' files"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kw):
|
||||||
|
_Extension.__init__(self, *args, **kw)
|
||||||
|
if not have_pyrex():
|
||||||
|
self._convert_pyx_sources_to_c()
|
||||||
|
|
||||||
|
def _convert_pyx_sources_to_c(self):
|
||||||
|
"convert .pyx extensions to .c"
|
||||||
|
def pyx_to_c(source):
|
||||||
|
if source.endswith('.pyx'):
|
||||||
|
source = source[:-4] + '.c'
|
||||||
|
return source
|
||||||
|
self.sources = map(pyx_to_c, self.sources)
|
||||||
|
|
||||||
|
class Library(Extension):
|
||||||
|
"""Just like a regular Extension, but built as a library instead"""
|
||||||
|
|
||||||
|
distutils.core.Extension = Extension
|
||||||
|
distutils.extension.Extension = Extension
|
||||||
|
if 'distutils.command.build_ext' in sys.modules:
|
||||||
|
sys.modules['distutils.command.build_ext'].Extension = Extension
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,920 @@
|
|||||||
|
"""PyPI and direct package downloading"""
|
||||||
|
import sys, os.path, re, urlparse, urllib, urllib2, shutil, random, socket, cStringIO
|
||||||
|
import base64
|
||||||
|
import httplib
|
||||||
|
from pkg_resources import *
|
||||||
|
from distutils import log
|
||||||
|
from distutils.errors import DistutilsError
|
||||||
|
try:
|
||||||
|
from hashlib import md5
|
||||||
|
except ImportError:
|
||||||
|
from md5 import md5
|
||||||
|
from fnmatch import translate
|
||||||
|
|
||||||
|
EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
|
||||||
|
HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
|
||||||
|
# this is here to fix emacs' cruddy broken syntax highlighting
|
||||||
|
PYPI_MD5 = re.compile(
|
||||||
|
'<a href="([^"#]+)">([^<]+)</a>\n\s+\\(<a (?:title="MD5 hash"\n\s+)'
|
||||||
|
'href="[^?]+\?:action=show_md5&digest=([0-9a-f]{32})">md5</a>\\)'
|
||||||
|
)
|
||||||
|
URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match
|
||||||
|
EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'PackageIndex', 'distros_for_url', 'parse_bdist_wininst',
|
||||||
|
'interpret_distro_name',
|
||||||
|
]
|
||||||
|
|
||||||
|
_SOCKET_TIMEOUT = 15
|
||||||
|
|
||||||
|
def parse_bdist_wininst(name):
|
||||||
|
"""Return (base,pyversion) or (None,None) for possible .exe name"""
|
||||||
|
|
||||||
|
lower = name.lower()
|
||||||
|
base, py_ver, plat = None, None, None
|
||||||
|
|
||||||
|
if lower.endswith('.exe'):
|
||||||
|
if lower.endswith('.win32.exe'):
|
||||||
|
base = name[:-10]
|
||||||
|
plat = 'win32'
|
||||||
|
elif lower.startswith('.win32-py',-16):
|
||||||
|
py_ver = name[-7:-4]
|
||||||
|
base = name[:-16]
|
||||||
|
plat = 'win32'
|
||||||
|
elif lower.endswith('.win-amd64.exe'):
|
||||||
|
base = name[:-14]
|
||||||
|
plat = 'win-amd64'
|
||||||
|
elif lower.startswith('.win-amd64-py',-20):
|
||||||
|
py_ver = name[-7:-4]
|
||||||
|
base = name[:-20]
|
||||||
|
plat = 'win-amd64'
|
||||||
|
return base,py_ver,plat
|
||||||
|
|
||||||
|
|
||||||
|
def egg_info_for_url(url):
|
||||||
|
scheme, server, path, parameters, query, fragment = urlparse.urlparse(url)
|
||||||
|
base = urllib2.unquote(path.split('/')[-1])
|
||||||
|
if '#' in base: base, fragment = base.split('#',1)
|
||||||
|
return base,fragment
|
||||||
|
|
||||||
|
def distros_for_url(url, metadata=None):
|
||||||
|
"""Yield egg or source distribution objects that might be found at a URL"""
|
||||||
|
base, fragment = egg_info_for_url(url)
|
||||||
|
for dist in distros_for_location(url, base, metadata): yield dist
|
||||||
|
if fragment:
|
||||||
|
match = EGG_FRAGMENT.match(fragment)
|
||||||
|
if match:
|
||||||
|
for dist in interpret_distro_name(
|
||||||
|
url, match.group(1), metadata, precedence = CHECKOUT_DIST
|
||||||
|
):
|
||||||
|
yield dist
|
||||||
|
|
||||||
|
def distros_for_location(location, basename, metadata=None):
|
||||||
|
"""Yield egg or source distribution objects based on basename"""
|
||||||
|
if basename.endswith('.egg.zip'):
|
||||||
|
basename = basename[:-4] # strip the .zip
|
||||||
|
if basename.endswith('.egg') and '-' in basename:
|
||||||
|
# only one, unambiguous interpretation
|
||||||
|
return [Distribution.from_location(location, basename, metadata)]
|
||||||
|
|
||||||
|
if basename.endswith('.exe'):
|
||||||
|
win_base, py_ver, platform = parse_bdist_wininst(basename)
|
||||||
|
if win_base is not None:
|
||||||
|
return interpret_distro_name(
|
||||||
|
location, win_base, metadata, py_ver, BINARY_DIST, platform
|
||||||
|
)
|
||||||
|
|
||||||
|
# Try source distro extensions (.zip, .tgz, etc.)
|
||||||
|
#
|
||||||
|
for ext in EXTENSIONS:
|
||||||
|
if basename.endswith(ext):
|
||||||
|
basename = basename[:-len(ext)]
|
||||||
|
return interpret_distro_name(location, basename, metadata)
|
||||||
|
return [] # no extension matched
|
||||||
|
|
||||||
|
def distros_for_filename(filename, metadata=None):
|
||||||
|
"""Yield possible egg or source distribution objects based on a filename"""
|
||||||
|
return distros_for_location(
|
||||||
|
normalize_path(filename), os.path.basename(filename), metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def interpret_distro_name(location, basename, metadata,
|
||||||
|
py_version=None, precedence=SOURCE_DIST, platform=None
|
||||||
|
):
|
||||||
|
"""Generate alternative interpretations of a source distro name
|
||||||
|
|
||||||
|
Note: if `location` is a filesystem filename, you should call
|
||||||
|
``pkg_resources.normalize_path()`` on it before passing it to this
|
||||||
|
routine!
|
||||||
|
"""
|
||||||
|
# Generate alternative interpretations of a source distro name
|
||||||
|
# Because some packages are ambiguous as to name/versions split
|
||||||
|
# e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc.
|
||||||
|
# So, we generate each possible interepretation (e.g. "adns, python-1.1.0"
|
||||||
|
# "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice,
|
||||||
|
# the spurious interpretations should be ignored, because in the event
|
||||||
|
# there's also an "adns" package, the spurious "python-1.1.0" version will
|
||||||
|
# compare lower than any numeric version number, and is therefore unlikely
|
||||||
|
# to match a request for it. It's still a potential problem, though, and
|
||||||
|
# in the long run PyPI and the distutils should go for "safe" names and
|
||||||
|
# versions in distribution archive names (sdist and bdist).
|
||||||
|
|
||||||
|
parts = basename.split('-')
|
||||||
|
if not py_version:
|
||||||
|
for i,p in enumerate(parts[2:]):
|
||||||
|
if len(p)==5 and p.startswith('py2.'):
|
||||||
|
return # It's a bdist_dumb, not an sdist -- bail out
|
||||||
|
|
||||||
|
for p in range(1,len(parts)+1):
|
||||||
|
yield Distribution(
|
||||||
|
location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]),
|
||||||
|
py_version=py_version, precedence = precedence,
|
||||||
|
platform = platform
|
||||||
|
)
|
||||||
|
|
||||||
|
REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
|
||||||
|
# this line is here to fix emacs' cruddy broken syntax highlighting
|
||||||
|
|
||||||
|
def find_external_links(url, page):
|
||||||
|
"""Find rel="homepage" and rel="download" links in `page`, yielding URLs"""
|
||||||
|
|
||||||
|
for match in REL.finditer(page):
|
||||||
|
tag, rel = match.groups()
|
||||||
|
rels = map(str.strip, rel.lower().split(','))
|
||||||
|
if 'homepage' in rels or 'download' in rels:
|
||||||
|
for match in HREF.finditer(tag):
|
||||||
|
yield urlparse.urljoin(url, htmldecode(match.group(1)))
|
||||||
|
|
||||||
|
for tag in ("<th>Home Page", "<th>Download URL"):
|
||||||
|
pos = page.find(tag)
|
||||||
|
if pos!=-1:
|
||||||
|
match = HREF.search(page,pos)
|
||||||
|
if match:
|
||||||
|
yield urlparse.urljoin(url, htmldecode(match.group(1)))
|
||||||
|
|
||||||
|
user_agent = "Python-urllib/%s distribute/%s" % (
|
||||||
|
sys.version[:3], require('distribute')[0].version
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PackageIndex(Environment):
|
||||||
|
"""A distribution index that scans web pages for download URLs"""
|
||||||
|
|
||||||
|
def __init__(self, index_url="http://pypi.python.org/simple", hosts=('*',),
|
||||||
|
*args, **kw
|
||||||
|
):
|
||||||
|
Environment.__init__(self,*args,**kw)
|
||||||
|
self.index_url = index_url + "/"[:not index_url.endswith('/')]
|
||||||
|
self.scanned_urls = {}
|
||||||
|
self.fetched_urls = {}
|
||||||
|
self.package_pages = {}
|
||||||
|
self.allows = re.compile('|'.join(map(translate,hosts))).match
|
||||||
|
self.to_scan = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def process_url(self, url, retrieve=False):
|
||||||
|
"""Evaluate a URL as a possible download, and maybe retrieve it"""
|
||||||
|
if url in self.scanned_urls and not retrieve:
|
||||||
|
return
|
||||||
|
self.scanned_urls[url] = True
|
||||||
|
if not URL_SCHEME(url):
|
||||||
|
self.process_filename(url)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
dists = list(distros_for_url(url))
|
||||||
|
if dists:
|
||||||
|
if not self.url_ok(url):
|
||||||
|
return
|
||||||
|
self.debug("Found link: %s", url)
|
||||||
|
|
||||||
|
if dists or not retrieve or url in self.fetched_urls:
|
||||||
|
map(self.add, dists)
|
||||||
|
return # don't need the actual page
|
||||||
|
|
||||||
|
if not self.url_ok(url):
|
||||||
|
self.fetched_urls[url] = True
|
||||||
|
return
|
||||||
|
|
||||||
|
self.info("Reading %s", url)
|
||||||
|
f = self.open_url(url, "Download error on %s: %%s -- Some packages may not be found!" % url)
|
||||||
|
if f is None: return
|
||||||
|
self.fetched_urls[url] = self.fetched_urls[f.url] = True
|
||||||
|
|
||||||
|
if 'html' not in f.headers.get('content-type', '').lower():
|
||||||
|
f.close() # not html, we can't process it
|
||||||
|
return
|
||||||
|
|
||||||
|
base = f.url # handle redirects
|
||||||
|
page = f.read()
|
||||||
|
if not isinstance(page, str): # We are in Python 3 and got bytes. We want str.
|
||||||
|
if isinstance(f, urllib2.HTTPError):
|
||||||
|
# Errors have no charset, assume latin1:
|
||||||
|
charset = 'latin-1'
|
||||||
|
else:
|
||||||
|
charset = f.headers.get_param('charset') or 'latin-1'
|
||||||
|
page = page.decode(charset, "ignore")
|
||||||
|
f.close()
|
||||||
|
for match in HREF.finditer(page):
|
||||||
|
link = urlparse.urljoin(base, htmldecode(match.group(1)))
|
||||||
|
self.process_url(link)
|
||||||
|
if url.startswith(self.index_url) and getattr(f,'code',None)!=404:
|
||||||
|
page = self.process_index(url, page)
|
||||||
|
|
||||||
|
def process_filename(self, fn, nested=False):
|
||||||
|
# process filenames or directories
|
||||||
|
if not os.path.exists(fn):
|
||||||
|
self.warn("Not found: %s", fn)
|
||||||
|
return
|
||||||
|
|
||||||
|
if os.path.isdir(fn) and not nested:
|
||||||
|
path = os.path.realpath(fn)
|
||||||
|
for item in os.listdir(path):
|
||||||
|
self.process_filename(os.path.join(path,item), True)
|
||||||
|
|
||||||
|
dists = distros_for_filename(fn)
|
||||||
|
if dists:
|
||||||
|
self.debug("Found: %s", fn)
|
||||||
|
map(self.add, dists)
|
||||||
|
|
||||||
|
def url_ok(self, url, fatal=False):
|
||||||
|
s = URL_SCHEME(url)
|
||||||
|
if (s and s.group(1).lower()=='file') or self.allows(urlparse.urlparse(url)[1]):
|
||||||
|
return True
|
||||||
|
msg = "\nLink to % s ***BLOCKED*** by --allow-hosts\n"
|
||||||
|
if fatal:
|
||||||
|
raise DistutilsError(msg % url)
|
||||||
|
else:
|
||||||
|
self.warn(msg, url)
|
||||||
|
|
||||||
|
def scan_egg_links(self, search_path):
|
||||||
|
for item in search_path:
|
||||||
|
if os.path.isdir(item):
|
||||||
|
for entry in os.listdir(item):
|
||||||
|
if entry.endswith('.egg-link'):
|
||||||
|
self.scan_egg_link(item, entry)
|
||||||
|
|
||||||
|
def scan_egg_link(self, path, entry):
|
||||||
|
lines = filter(None, map(str.strip, open(os.path.join(path, entry))))
|
||||||
|
if len(lines)==2:
|
||||||
|
for dist in find_distributions(os.path.join(path, lines[0])):
|
||||||
|
dist.location = os.path.join(path, *lines)
|
||||||
|
dist.precedence = SOURCE_DIST
|
||||||
|
self.add(dist)
|
||||||
|
|
||||||
|
def process_index(self,url,page):
|
||||||
|
"""Process the contents of a PyPI page"""
|
||||||
|
def scan(link):
|
||||||
|
# Process a URL to see if it's for a package page
|
||||||
|
if link.startswith(self.index_url):
|
||||||
|
parts = map(
|
||||||
|
urllib2.unquote, link[len(self.index_url):].split('/')
|
||||||
|
)
|
||||||
|
if len(parts)==2 and '#' not in parts[1]:
|
||||||
|
# it's a package page, sanitize and index it
|
||||||
|
pkg = safe_name(parts[0])
|
||||||
|
ver = safe_version(parts[1])
|
||||||
|
self.package_pages.setdefault(pkg.lower(),{})[link] = True
|
||||||
|
return to_filename(pkg), to_filename(ver)
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
# process an index page into the package-page index
|
||||||
|
for match in HREF.finditer(page):
|
||||||
|
try:
|
||||||
|
scan( urlparse.urljoin(url, htmldecode(match.group(1))) )
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
pkg, ver = scan(url) # ensure this page is in the page index
|
||||||
|
if pkg:
|
||||||
|
# process individual package page
|
||||||
|
for new_url in find_external_links(url, page):
|
||||||
|
# Process the found URL
|
||||||
|
base, frag = egg_info_for_url(new_url)
|
||||||
|
if base.endswith('.py') and not frag:
|
||||||
|
if ver:
|
||||||
|
new_url+='#egg=%s-%s' % (pkg,ver)
|
||||||
|
else:
|
||||||
|
self.need_version_info(url)
|
||||||
|
self.scan_url(new_url)
|
||||||
|
|
||||||
|
return PYPI_MD5.sub(
|
||||||
|
lambda m: '<a href="%s#md5=%s">%s</a>' % m.group(1,3,2), page
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return "" # no sense double-scanning non-package pages
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def need_version_info(self, url):
|
||||||
|
self.scan_all(
|
||||||
|
"Page at %s links to .py file(s) without version info; an index "
|
||||||
|
"scan is required.", url
|
||||||
|
)
|
||||||
|
|
||||||
|
def scan_all(self, msg=None, *args):
|
||||||
|
if self.index_url not in self.fetched_urls:
|
||||||
|
if msg: self.warn(msg,*args)
|
||||||
|
self.info(
|
||||||
|
"Scanning index of all packages (this may take a while)"
|
||||||
|
)
|
||||||
|
self.scan_url(self.index_url)
|
||||||
|
|
||||||
|
def find_packages(self, requirement):
|
||||||
|
self.scan_url(self.index_url + requirement.unsafe_name+'/')
|
||||||
|
|
||||||
|
if not self.package_pages.get(requirement.key):
|
||||||
|
# Fall back to safe version of the name
|
||||||
|
self.scan_url(self.index_url + requirement.project_name+'/')
|
||||||
|
|
||||||
|
if not self.package_pages.get(requirement.key):
|
||||||
|
# We couldn't find the target package, so search the index page too
|
||||||
|
self.not_found_in_index(requirement)
|
||||||
|
|
||||||
|
for url in list(self.package_pages.get(requirement.key,())):
|
||||||
|
# scan each page that might be related to the desired package
|
||||||
|
self.scan_url(url)
|
||||||
|
|
||||||
|
def obtain(self, requirement, installer=None):
|
||||||
|
self.prescan(); self.find_packages(requirement)
|
||||||
|
for dist in self[requirement.key]:
|
||||||
|
if dist in requirement:
|
||||||
|
return dist
|
||||||
|
self.debug("%s does not match %s", requirement, dist)
|
||||||
|
return super(PackageIndex, self).obtain(requirement,installer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def check_md5(self, cs, info, filename, tfp):
|
||||||
|
if re.match('md5=[0-9a-f]{32}$', info):
|
||||||
|
self.debug("Validating md5 checksum for %s", filename)
|
||||||
|
if cs.hexdigest()<>info[4:]:
|
||||||
|
tfp.close()
|
||||||
|
os.unlink(filename)
|
||||||
|
raise DistutilsError(
|
||||||
|
"MD5 validation failed for "+os.path.basename(filename)+
|
||||||
|
"; possible download problem?"
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_find_links(self, urls):
|
||||||
|
"""Add `urls` to the list that will be prescanned for searches"""
|
||||||
|
for url in urls:
|
||||||
|
if (
|
||||||
|
self.to_scan is None # if we have already "gone online"
|
||||||
|
or not URL_SCHEME(url) # or it's a local file/directory
|
||||||
|
or url.startswith('file:')
|
||||||
|
or list(distros_for_url(url)) # or a direct package link
|
||||||
|
):
|
||||||
|
# then go ahead and process it now
|
||||||
|
self.scan_url(url)
|
||||||
|
else:
|
||||||
|
# otherwise, defer retrieval till later
|
||||||
|
self.to_scan.append(url)
|
||||||
|
|
||||||
|
def prescan(self):
|
||||||
|
"""Scan urls scheduled for prescanning (e.g. --find-links)"""
|
||||||
|
if self.to_scan:
|
||||||
|
map(self.scan_url, self.to_scan)
|
||||||
|
self.to_scan = None # from now on, go ahead and process immediately
|
||||||
|
|
||||||
|
def not_found_in_index(self, requirement):
|
||||||
|
if self[requirement.key]: # we've seen at least one distro
|
||||||
|
meth, msg = self.info, "Couldn't retrieve index page for %r"
|
||||||
|
else: # no distros seen for this name, might be misspelled
|
||||||
|
meth, msg = (self.warn,
|
||||||
|
"Couldn't find index page for %r (maybe misspelled?)")
|
||||||
|
meth(msg, requirement.unsafe_name)
|
||||||
|
self.scan_all()
|
||||||
|
|
||||||
|
def download(self, spec, tmpdir):
|
||||||
|
"""Locate and/or download `spec` to `tmpdir`, returning a local path
|
||||||
|
|
||||||
|
`spec` may be a ``Requirement`` object, or a string containing a URL,
|
||||||
|
an existing local filename, or a project/version requirement spec
|
||||||
|
(i.e. the string form of a ``Requirement`` object). If it is the URL
|
||||||
|
of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one
|
||||||
|
that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is
|
||||||
|
automatically created alongside the downloaded file.
|
||||||
|
|
||||||
|
If `spec` is a ``Requirement`` object or a string containing a
|
||||||
|
project/version requirement spec, this method returns the location of
|
||||||
|
a matching distribution (possibly after downloading it to `tmpdir`).
|
||||||
|
If `spec` is a locally existing file or directory name, it is simply
|
||||||
|
returned unchanged. If `spec` is a URL, it is downloaded to a subpath
|
||||||
|
of `tmpdir`, and the local filename is returned. Various errors may be
|
||||||
|
raised if a problem occurs during downloading.
|
||||||
|
"""
|
||||||
|
if not isinstance(spec,Requirement):
|
||||||
|
scheme = URL_SCHEME(spec)
|
||||||
|
if scheme:
|
||||||
|
# It's a url, download it to tmpdir
|
||||||
|
found = self._download_url(scheme.group(1), spec, tmpdir)
|
||||||
|
base, fragment = egg_info_for_url(spec)
|
||||||
|
if base.endswith('.py'):
|
||||||
|
found = self.gen_setup(found,fragment,tmpdir)
|
||||||
|
return found
|
||||||
|
elif os.path.exists(spec):
|
||||||
|
# Existing file or directory, just return it
|
||||||
|
return spec
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
spec = Requirement.parse(spec)
|
||||||
|
except ValueError:
|
||||||
|
raise DistutilsError(
|
||||||
|
"Not a URL, existing file, or requirement spec: %r" %
|
||||||
|
(spec,)
|
||||||
|
)
|
||||||
|
return getattr(self.fetch_distribution(spec, tmpdir),'location',None)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_distribution(self,
|
||||||
|
requirement, tmpdir, force_scan=False, source=False, develop_ok=False,
|
||||||
|
local_index=None
|
||||||
|
):
|
||||||
|
"""Obtain a distribution suitable for fulfilling `requirement`
|
||||||
|
|
||||||
|
`requirement` must be a ``pkg_resources.Requirement`` instance.
|
||||||
|
If necessary, or if the `force_scan` flag is set, the requirement is
|
||||||
|
searched for in the (online) package index as well as the locally
|
||||||
|
installed packages. If a distribution matching `requirement` is found,
|
||||||
|
the returned distribution's ``location`` is the value you would have
|
||||||
|
gotten from calling the ``download()`` method with the matching
|
||||||
|
distribution's URL or filename. If no matching distribution is found,
|
||||||
|
``None`` is returned.
|
||||||
|
|
||||||
|
If the `source` flag is set, only source distributions and source
|
||||||
|
checkout links will be considered. Unless the `develop_ok` flag is
|
||||||
|
set, development and system eggs (i.e., those using the ``.egg-info``
|
||||||
|
format) will be ignored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# process a Requirement
|
||||||
|
self.info("Searching for %s", requirement)
|
||||||
|
skipped = {}
|
||||||
|
dist = None
|
||||||
|
|
||||||
|
def find(req, env=None):
|
||||||
|
if env is None:
|
||||||
|
env = self
|
||||||
|
# Find a matching distribution; may be called more than once
|
||||||
|
|
||||||
|
for dist in env[req.key]:
|
||||||
|
|
||||||
|
if dist.precedence==DEVELOP_DIST and not develop_ok:
|
||||||
|
if dist not in skipped:
|
||||||
|
self.warn("Skipping development or system egg: %s",dist)
|
||||||
|
skipped[dist] = 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if dist in req and (dist.precedence<=SOURCE_DIST or not source):
|
||||||
|
self.info("Best match: %s", dist)
|
||||||
|
return dist.clone(
|
||||||
|
location=self.download(dist.location, tmpdir)
|
||||||
|
)
|
||||||
|
|
||||||
|
if force_scan:
|
||||||
|
self.prescan()
|
||||||
|
self.find_packages(requirement)
|
||||||
|
dist = find(requirement)
|
||||||
|
|
||||||
|
if local_index is not None:
|
||||||
|
dist = dist or find(requirement, local_index)
|
||||||
|
|
||||||
|
if dist is None and self.to_scan is not None:
|
||||||
|
self.prescan()
|
||||||
|
dist = find(requirement)
|
||||||
|
|
||||||
|
if dist is None and not force_scan:
|
||||||
|
self.find_packages(requirement)
|
||||||
|
dist = find(requirement)
|
||||||
|
|
||||||
|
if dist is None:
|
||||||
|
self.warn(
|
||||||
|
"No local packages or download links found for %s%s",
|
||||||
|
(source and "a source distribution of " or ""),
|
||||||
|
requirement,
|
||||||
|
)
|
||||||
|
return dist
|
||||||
|
|
||||||
|
def fetch(self, requirement, tmpdir, force_scan=False, source=False):
|
||||||
|
"""Obtain a file suitable for fulfilling `requirement`
|
||||||
|
|
||||||
|
DEPRECATED; use the ``fetch_distribution()`` method now instead. For
|
||||||
|
backward compatibility, this routine is identical but returns the
|
||||||
|
``location`` of the downloaded distribution instead of a distribution
|
||||||
|
object.
|
||||||
|
"""
|
||||||
|
dist = self.fetch_distribution(requirement,tmpdir,force_scan,source)
|
||||||
|
if dist is not None:
|
||||||
|
return dist.location
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def gen_setup(self, filename, fragment, tmpdir):
|
||||||
|
match = EGG_FRAGMENT.match(fragment)
|
||||||
|
dists = match and [d for d in
|
||||||
|
interpret_distro_name(filename, match.group(1), None) if d.version
|
||||||
|
] or []
|
||||||
|
|
||||||
|
if len(dists)==1: # unambiguous ``#egg`` fragment
|
||||||
|
basename = os.path.basename(filename)
|
||||||
|
|
||||||
|
# Make sure the file has been downloaded to the temp dir.
|
||||||
|
if os.path.dirname(filename) != tmpdir:
|
||||||
|
dst = os.path.join(tmpdir, basename)
|
||||||
|
from setuptools.command.easy_install import samefile
|
||||||
|
if not samefile(filename, dst):
|
||||||
|
shutil.copy2(filename, dst)
|
||||||
|
filename=dst
|
||||||
|
|
||||||
|
file = open(os.path.join(tmpdir, 'setup.py'), 'w')
|
||||||
|
file.write(
|
||||||
|
"from setuptools import setup\n"
|
||||||
|
"setup(name=%r, version=%r, py_modules=[%r])\n"
|
||||||
|
% (
|
||||||
|
dists[0].project_name, dists[0].version,
|
||||||
|
os.path.splitext(basename)[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
file.close()
|
||||||
|
return filename
|
||||||
|
|
||||||
|
elif match:
|
||||||
|
raise DistutilsError(
|
||||||
|
"Can't unambiguously interpret project/version identifier %r; "
|
||||||
|
"any dashes in the name or version should be escaped using "
|
||||||
|
"underscores. %r" % (fragment,dists)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise DistutilsError(
|
||||||
|
"Can't process plain .py files without an '#egg=name-version'"
|
||||||
|
" suffix to enable automatic setup script generation."
|
||||||
|
)
|
||||||
|
|
||||||
|
dl_blocksize = 8192
|
||||||
|
def _download_to(self, url, filename):
|
||||||
|
self.info("Downloading %s", url)
|
||||||
|
# Download the file
|
||||||
|
fp, tfp, info = None, None, None
|
||||||
|
try:
|
||||||
|
if '#' in url:
|
||||||
|
url, info = url.split('#', 1)
|
||||||
|
fp = self.open_url(url)
|
||||||
|
if isinstance(fp, urllib2.HTTPError):
|
||||||
|
raise DistutilsError(
|
||||||
|
"Can't download %s: %s %s" % (url, fp.code,fp.msg)
|
||||||
|
)
|
||||||
|
cs = md5()
|
||||||
|
headers = fp.info()
|
||||||
|
blocknum = 0
|
||||||
|
bs = self.dl_blocksize
|
||||||
|
size = -1
|
||||||
|
if "content-length" in headers:
|
||||||
|
# Some servers return multiple Content-Length headers :(
|
||||||
|
content_length = headers.get("Content-Length")
|
||||||
|
size = int(content_length)
|
||||||
|
self.reporthook(url, filename, blocknum, bs, size)
|
||||||
|
tfp = open(filename,'wb')
|
||||||
|
while True:
|
||||||
|
block = fp.read(bs)
|
||||||
|
if block:
|
||||||
|
cs.update(block)
|
||||||
|
tfp.write(block)
|
||||||
|
blocknum += 1
|
||||||
|
self.reporthook(url, filename, blocknum, bs, size)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if info: self.check_md5(cs, info, filename, tfp)
|
||||||
|
return headers
|
||||||
|
finally:
|
||||||
|
if fp: fp.close()
|
||||||
|
if tfp: tfp.close()
|
||||||
|
|
||||||
|
def reporthook(self, url, filename, blocknum, blksize, size):
|
||||||
|
pass # no-op
|
||||||
|
|
||||||
|
|
||||||
|
def open_url(self, url, warning=None):
|
||||||
|
if url.startswith('file:'):
|
||||||
|
return local_open(url)
|
||||||
|
try:
|
||||||
|
return open_with_auth(url)
|
||||||
|
except (ValueError, httplib.InvalidURL), v:
|
||||||
|
msg = ' '.join([str(arg) for arg in v.args])
|
||||||
|
if warning:
|
||||||
|
self.warn(warning, msg)
|
||||||
|
else:
|
||||||
|
raise DistutilsError('%s %s' % (url, msg))
|
||||||
|
except urllib2.HTTPError, v:
|
||||||
|
return v
|
||||||
|
except urllib2.URLError, v:
|
||||||
|
if warning:
|
||||||
|
self.warn(warning, v.reason)
|
||||||
|
else:
|
||||||
|
raise DistutilsError("Download error for %s: %s"
|
||||||
|
% (url, v.reason))
|
||||||
|
except httplib.BadStatusLine, v:
|
||||||
|
if warning:
|
||||||
|
self.warn(warning, v.line)
|
||||||
|
else:
|
||||||
|
raise DistutilsError('%s returned a bad status line. '
|
||||||
|
'The server might be down, %s' % \
|
||||||
|
(url, v.line))
|
||||||
|
except httplib.HTTPException, v:
|
||||||
|
if warning:
|
||||||
|
self.warn(warning, v)
|
||||||
|
else:
|
||||||
|
raise DistutilsError("Download error for %s: %s"
|
||||||
|
% (url, v))
|
||||||
|
|
||||||
|
def _download_url(self, scheme, url, tmpdir):
|
||||||
|
# Determine download filename
|
||||||
|
#
|
||||||
|
name = filter(None,urlparse.urlparse(url)[2].split('/'))
|
||||||
|
if name:
|
||||||
|
name = name[-1]
|
||||||
|
while '..' in name:
|
||||||
|
name = name.replace('..','.').replace('\\','_')
|
||||||
|
else:
|
||||||
|
name = "__downloaded__" # default if URL has no path contents
|
||||||
|
|
||||||
|
if name.endswith('.egg.zip'):
|
||||||
|
name = name[:-4] # strip the extra .zip before download
|
||||||
|
|
||||||
|
filename = os.path.join(tmpdir,name)
|
||||||
|
|
||||||
|
# Download the file
|
||||||
|
#
|
||||||
|
if scheme=='svn' or scheme.startswith('svn+'):
|
||||||
|
return self._download_svn(url, filename)
|
||||||
|
elif scheme=='git' or scheme.startswith('git+'):
|
||||||
|
return self._download_git(url, filename)
|
||||||
|
elif scheme.startswith('hg+'):
|
||||||
|
return self._download_hg(url, filename)
|
||||||
|
elif scheme=='file':
|
||||||
|
return urllib.url2pathname(urlparse.urlparse(url)[2])
|
||||||
|
else:
|
||||||
|
self.url_ok(url, True) # raises error if not allowed
|
||||||
|
return self._attempt_download(url, filename)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def scan_url(self, url):
|
||||||
|
self.process_url(url, True)
|
||||||
|
|
||||||
|
|
||||||
|
def _attempt_download(self, url, filename):
|
||||||
|
headers = self._download_to(url, filename)
|
||||||
|
if 'html' in headers.get('content-type','').lower():
|
||||||
|
return self._download_html(url, headers, filename)
|
||||||
|
else:
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def _download_html(self, url, headers, filename):
|
||||||
|
file = open(filename)
|
||||||
|
for line in file:
|
||||||
|
if line.strip():
|
||||||
|
# Check for a subversion index page
|
||||||
|
if re.search(r'<title>([^- ]+ - )?Revision \d+:', line):
|
||||||
|
# it's a subversion index page:
|
||||||
|
file.close()
|
||||||
|
os.unlink(filename)
|
||||||
|
return self._download_svn(url, filename)
|
||||||
|
break # not an index page
|
||||||
|
file.close()
|
||||||
|
os.unlink(filename)
|
||||||
|
raise DistutilsError("Unexpected HTML page found at "+url)
|
||||||
|
|
||||||
|
def _download_svn(self, url, filename):
|
||||||
|
url = url.split('#',1)[0] # remove any fragment for svn's sake
|
||||||
|
self.info("Doing subversion checkout from %s to %s", url, filename)
|
||||||
|
os.system("svn checkout -q %s %s" % (url, filename))
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def _vcs_split_rev_from_url(self, url, pop_prefix=False):
|
||||||
|
scheme, netloc, path, query, frag = urlparse.urlsplit(url)
|
||||||
|
|
||||||
|
scheme = scheme.split('+', 1)[-1]
|
||||||
|
|
||||||
|
# Some fragment identification fails
|
||||||
|
path = path.split('#',1)[0]
|
||||||
|
|
||||||
|
rev = None
|
||||||
|
if '@' in path:
|
||||||
|
path, rev = path.rsplit('@', 1)
|
||||||
|
|
||||||
|
# Also, discard fragment
|
||||||
|
url = urlparse.urlunsplit((scheme, netloc, path, query, ''))
|
||||||
|
|
||||||
|
return url, rev
|
||||||
|
|
||||||
|
def _download_git(self, url, filename):
|
||||||
|
filename = filename.split('#',1)[0]
|
||||||
|
url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
|
||||||
|
|
||||||
|
self.info("Doing git clone from %s to %s", url, filename)
|
||||||
|
os.system("git clone --quiet %s %s" % (url, filename))
|
||||||
|
|
||||||
|
if rev is not None:
|
||||||
|
self.info("Checking out %s", rev)
|
||||||
|
os.system("(cd %s && git checkout --quiet %s)" % (
|
||||||
|
filename,
|
||||||
|
rev,
|
||||||
|
))
|
||||||
|
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def _download_hg(self, url, filename):
|
||||||
|
filename = filename.split('#',1)[0]
|
||||||
|
url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
|
||||||
|
|
||||||
|
self.info("Doing hg clone from %s to %s", url, filename)
|
||||||
|
os.system("hg clone --quiet %s %s" % (url, filename))
|
||||||
|
|
||||||
|
if rev is not None:
|
||||||
|
self.info("Updating to %s", rev)
|
||||||
|
os.system("(cd %s && hg up -C -r %s >&-)" % (
|
||||||
|
filename,
|
||||||
|
rev,
|
||||||
|
))
|
||||||
|
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def debug(self, msg, *args):
|
||||||
|
log.debug(msg, *args)
|
||||||
|
|
||||||
|
def info(self, msg, *args):
|
||||||
|
log.info(msg, *args)
|
||||||
|
|
||||||
|
def warn(self, msg, *args):
|
||||||
|
log.warn(msg, *args)
|
||||||
|
|
||||||
|
# This pattern matches a character entity reference (a decimal numeric
|
||||||
|
# references, a hexadecimal numeric reference, or a named reference).
|
||||||
|
entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
|
||||||
|
|
||||||
|
def uchr(c):
|
||||||
|
if not isinstance(c, int):
|
||||||
|
return c
|
||||||
|
if c>255: return unichr(c)
|
||||||
|
return chr(c)
|
||||||
|
|
||||||
|
def decode_entity(match):
|
||||||
|
what = match.group(1)
|
||||||
|
if what.startswith('#x'):
|
||||||
|
what = int(what[2:], 16)
|
||||||
|
elif what.startswith('#'):
|
||||||
|
what = int(what[1:])
|
||||||
|
else:
|
||||||
|
from htmlentitydefs import name2codepoint
|
||||||
|
what = name2codepoint.get(what, match.group(0))
|
||||||
|
return uchr(what)
|
||||||
|
|
||||||
|
def htmldecode(text):
|
||||||
|
"""Decode HTML entities in the given text."""
|
||||||
|
return entity_sub(decode_entity, text)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def socket_timeout(timeout=15):
|
||||||
|
def _socket_timeout(func):
|
||||||
|
def _socket_timeout(*args, **kwargs):
|
||||||
|
old_timeout = socket.getdefaulttimeout()
|
||||||
|
socket.setdefaulttimeout(timeout)
|
||||||
|
try:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
finally:
|
||||||
|
socket.setdefaulttimeout(old_timeout)
|
||||||
|
return _socket_timeout
|
||||||
|
return _socket_timeout
|
||||||
|
|
||||||
|
def _encode_auth(auth):
|
||||||
|
"""
|
||||||
|
A function compatible with Python 2.3-3.3 that will encode
|
||||||
|
auth from a URL suitable for an HTTP header.
|
||||||
|
>>> _encode_auth('username%3Apassword')
|
||||||
|
u'dXNlcm5hbWU6cGFzc3dvcmQ='
|
||||||
|
"""
|
||||||
|
auth_s = urllib2.unquote(auth)
|
||||||
|
# convert to bytes
|
||||||
|
auth_bytes = auth_s.encode()
|
||||||
|
# use the legacy interface for Python 2.3 support
|
||||||
|
encoded_bytes = base64.encodestring(auth_bytes)
|
||||||
|
# convert back to a string
|
||||||
|
encoded = encoded_bytes.decode()
|
||||||
|
# strip the trailing carriage return
|
||||||
|
return encoded.rstrip()
|
||||||
|
|
||||||
|
def open_with_auth(url):
|
||||||
|
"""Open a urllib2 request, handling HTTP authentication"""
|
||||||
|
|
||||||
|
scheme, netloc, path, params, query, frag = urlparse.urlparse(url)
|
||||||
|
|
||||||
|
# Double scheme does not raise on Mac OS X as revealed by a
|
||||||
|
# failing test. We would expect "nonnumeric port". Refs #20.
|
||||||
|
if netloc.endswith(':'):
|
||||||
|
raise httplib.InvalidURL("nonnumeric port: ''")
|
||||||
|
|
||||||
|
if scheme in ('http', 'https'):
|
||||||
|
auth, host = urllib2.splituser(netloc)
|
||||||
|
else:
|
||||||
|
auth = None
|
||||||
|
|
||||||
|
if auth:
|
||||||
|
auth = "Basic " + _encode_auth(auth)
|
||||||
|
new_url = urlparse.urlunparse((scheme,host,path,params,query,frag))
|
||||||
|
request = urllib2.Request(new_url)
|
||||||
|
request.add_header("Authorization", auth)
|
||||||
|
else:
|
||||||
|
request = urllib2.Request(url)
|
||||||
|
|
||||||
|
request.add_header('User-Agent', user_agent)
|
||||||
|
fp = urllib2.urlopen(request)
|
||||||
|
|
||||||
|
if auth:
|
||||||
|
# Put authentication info back into request URL if same host,
|
||||||
|
# so that links found on the page will work
|
||||||
|
s2, h2, path2, param2, query2, frag2 = urlparse.urlparse(fp.url)
|
||||||
|
if s2==scheme and h2==host:
|
||||||
|
fp.url = urlparse.urlunparse((s2,netloc,path2,param2,query2,frag2))
|
||||||
|
|
||||||
|
return fp
|
||||||
|
|
||||||
|
# adding a timeout to avoid freezing package_index
|
||||||
|
open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def fix_sf_url(url):
|
||||||
|
return url # backward compatibility
|
||||||
|
|
||||||
|
def local_open(url):
|
||||||
|
"""Read a local path, with special support for directories"""
|
||||||
|
scheme, server, path, param, query, frag = urlparse.urlparse(url)
|
||||||
|
filename = urllib.url2pathname(path)
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
return urllib2.urlopen(url)
|
||||||
|
elif path.endswith('/') and os.path.isdir(filename):
|
||||||
|
files = []
|
||||||
|
for f in os.listdir(filename):
|
||||||
|
if f=='index.html':
|
||||||
|
fp = open(os.path.join(filename,f),'rb')
|
||||||
|
body = fp.read()
|
||||||
|
fp.close()
|
||||||
|
break
|
||||||
|
elif os.path.isdir(os.path.join(filename,f)):
|
||||||
|
f+='/'
|
||||||
|
files.append("<a href=%r>%s</a>" % (f,f))
|
||||||
|
else:
|
||||||
|
body = ("<html><head><title>%s</title>" % url) + \
|
||||||
|
"</head><body>%s</body></html>" % '\n'.join(files)
|
||||||
|
status, message = 200, "OK"
|
||||||
|
else:
|
||||||
|
status, message, body = 404, "Path not found", "Not found"
|
||||||
|
|
||||||
|
return urllib2.HTTPError(url, status, message,
|
||||||
|
{'content-type':'text/html'}, cStringIO.StringIO(body))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# this line is a kludge to keep the trailing blank lines for pje's editor
|
||||||
+293
@@ -0,0 +1,293 @@
|
|||||||
|
import os, sys, __builtin__, tempfile, operator, pkg_resources
|
||||||
|
if os.name == "java":
|
||||||
|
import org.python.modules.posix.PosixModule as _os
|
||||||
|
else:
|
||||||
|
_os = sys.modules[os.name]
|
||||||
|
try:
|
||||||
|
_file = file
|
||||||
|
except NameError:
|
||||||
|
_file = None
|
||||||
|
_open = open
|
||||||
|
from distutils.errors import DistutilsError
|
||||||
|
__all__ = [
|
||||||
|
"AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup",
|
||||||
|
]
|
||||||
|
def run_setup(setup_script, args):
|
||||||
|
"""Run a distutils setup script, sandboxed in its directory"""
|
||||||
|
old_dir = os.getcwd()
|
||||||
|
save_argv = sys.argv[:]
|
||||||
|
save_path = sys.path[:]
|
||||||
|
setup_dir = os.path.abspath(os.path.dirname(setup_script))
|
||||||
|
temp_dir = os.path.join(setup_dir,'temp')
|
||||||
|
if not os.path.isdir(temp_dir): os.makedirs(temp_dir)
|
||||||
|
save_tmp = tempfile.tempdir
|
||||||
|
save_modules = sys.modules.copy()
|
||||||
|
pr_state = pkg_resources.__getstate__()
|
||||||
|
try:
|
||||||
|
tempfile.tempdir = temp_dir
|
||||||
|
os.chdir(setup_dir)
|
||||||
|
try:
|
||||||
|
sys.argv[:] = [setup_script]+list(args)
|
||||||
|
sys.path.insert(0, setup_dir)
|
||||||
|
DirectorySandbox(setup_dir).run(
|
||||||
|
lambda: execfile(
|
||||||
|
"setup.py",
|
||||||
|
{'__file__':setup_script, '__name__':'__main__'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except SystemExit, v:
|
||||||
|
if v.args and v.args[0]:
|
||||||
|
raise
|
||||||
|
# Normal exit, just return
|
||||||
|
finally:
|
||||||
|
pkg_resources.__setstate__(pr_state)
|
||||||
|
sys.modules.update(save_modules)
|
||||||
|
# remove any modules imported within the sandbox
|
||||||
|
del_modules = [
|
||||||
|
mod_name for mod_name in sys.modules
|
||||||
|
if mod_name not in save_modules
|
||||||
|
# exclude any encodings modules. See #285
|
||||||
|
and not mod_name.startswith('encodings.')
|
||||||
|
]
|
||||||
|
map(sys.modules.__delitem__, del_modules)
|
||||||
|
os.chdir(old_dir)
|
||||||
|
sys.path[:] = save_path
|
||||||
|
sys.argv[:] = save_argv
|
||||||
|
tempfile.tempdir = save_tmp
|
||||||
|
|
||||||
|
class AbstractSandbox:
|
||||||
|
"""Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
|
||||||
|
|
||||||
|
_active = False
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._attrs = [
|
||||||
|
name for name in dir(_os)
|
||||||
|
if not name.startswith('_') and hasattr(self,name)
|
||||||
|
]
|
||||||
|
|
||||||
|
def _copy(self, source):
|
||||||
|
for name in self._attrs:
|
||||||
|
setattr(os, name, getattr(source,name))
|
||||||
|
|
||||||
|
def run(self, func):
|
||||||
|
"""Run 'func' under os sandboxing"""
|
||||||
|
try:
|
||||||
|
self._copy(self)
|
||||||
|
if _file:
|
||||||
|
__builtin__.file = self._file
|
||||||
|
__builtin__.open = self._open
|
||||||
|
self._active = True
|
||||||
|
return func()
|
||||||
|
finally:
|
||||||
|
self._active = False
|
||||||
|
if _file:
|
||||||
|
__builtin__.file = _file
|
||||||
|
__builtin__.open = _open
|
||||||
|
self._copy(_os)
|
||||||
|
|
||||||
|
|
||||||
|
def _mk_dual_path_wrapper(name):
|
||||||
|
original = getattr(_os,name)
|
||||||
|
def wrap(self,src,dst,*args,**kw):
|
||||||
|
if self._active:
|
||||||
|
src,dst = self._remap_pair(name,src,dst,*args,**kw)
|
||||||
|
return original(src,dst,*args,**kw)
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
|
||||||
|
for name in ["rename", "link", "symlink"]:
|
||||||
|
if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name)
|
||||||
|
|
||||||
|
|
||||||
|
def _mk_single_path_wrapper(name, original=None):
|
||||||
|
original = original or getattr(_os,name)
|
||||||
|
def wrap(self,path,*args,**kw):
|
||||||
|
if self._active:
|
||||||
|
path = self._remap_input(name,path,*args,**kw)
|
||||||
|
return original(path,*args,**kw)
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
if _file:
|
||||||
|
_file = _mk_single_path_wrapper('file', _file)
|
||||||
|
_open = _mk_single_path_wrapper('open', _open)
|
||||||
|
for name in [
|
||||||
|
"stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir",
|
||||||
|
"remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat",
|
||||||
|
"startfile", "mkfifo", "mknod", "pathconf", "access"
|
||||||
|
]:
|
||||||
|
if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name)
|
||||||
|
|
||||||
|
|
||||||
|
def _mk_single_with_return(name):
|
||||||
|
original = getattr(_os,name)
|
||||||
|
def wrap(self,path,*args,**kw):
|
||||||
|
if self._active:
|
||||||
|
path = self._remap_input(name,path,*args,**kw)
|
||||||
|
return self._remap_output(name, original(path,*args,**kw))
|
||||||
|
return original(path,*args,**kw)
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
for name in ['readlink', 'tempnam']:
|
||||||
|
if hasattr(_os,name): locals()[name] = _mk_single_with_return(name)
|
||||||
|
|
||||||
|
def _mk_query(name):
|
||||||
|
original = getattr(_os,name)
|
||||||
|
def wrap(self,*args,**kw):
|
||||||
|
retval = original(*args,**kw)
|
||||||
|
if self._active:
|
||||||
|
return self._remap_output(name, retval)
|
||||||
|
return retval
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
for name in ['getcwd', 'tmpnam']:
|
||||||
|
if hasattr(_os,name): locals()[name] = _mk_query(name)
|
||||||
|
|
||||||
|
def _validate_path(self,path):
|
||||||
|
"""Called to remap or validate any path, whether input or output"""
|
||||||
|
return path
|
||||||
|
|
||||||
|
def _remap_input(self,operation,path,*args,**kw):
|
||||||
|
"""Called for path inputs"""
|
||||||
|
return self._validate_path(path)
|
||||||
|
|
||||||
|
def _remap_output(self,operation,path):
|
||||||
|
"""Called for path outputs"""
|
||||||
|
return self._validate_path(path)
|
||||||
|
|
||||||
|
def _remap_pair(self,operation,src,dst,*args,**kw):
|
||||||
|
"""Called for path pairs like rename, link, and symlink operations"""
|
||||||
|
return (
|
||||||
|
self._remap_input(operation+'-from',src,*args,**kw),
|
||||||
|
self._remap_input(operation+'-to',dst,*args,**kw)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(os, 'devnull'):
|
||||||
|
_EXCEPTIONS = [os.devnull,]
|
||||||
|
else:
|
||||||
|
_EXCEPTIONS = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
from win32com.client.gencache import GetGeneratePath
|
||||||
|
_EXCEPTIONS.append(GetGeneratePath())
|
||||||
|
del GetGeneratePath
|
||||||
|
except ImportError:
|
||||||
|
# it appears pywin32 is not installed, so no need to exclude.
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DirectorySandbox(AbstractSandbox):
|
||||||
|
"""Restrict operations to a single subdirectory - pseudo-chroot"""
|
||||||
|
|
||||||
|
write_ops = dict.fromkeys([
|
||||||
|
"open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir",
|
||||||
|
"utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam",
|
||||||
|
])
|
||||||
|
|
||||||
|
def __init__(self, sandbox, exceptions=_EXCEPTIONS):
|
||||||
|
self._sandbox = os.path.normcase(os.path.realpath(sandbox))
|
||||||
|
self._prefix = os.path.join(self._sandbox,'')
|
||||||
|
self._exceptions = [os.path.normcase(os.path.realpath(path)) for path in exceptions]
|
||||||
|
AbstractSandbox.__init__(self)
|
||||||
|
|
||||||
|
def _violation(self, operation, *args, **kw):
|
||||||
|
raise SandboxViolation(operation, args, kw)
|
||||||
|
|
||||||
|
if _file:
|
||||||
|
def _file(self, path, mode='r', *args, **kw):
|
||||||
|
if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
|
||||||
|
self._violation("file", path, mode, *args, **kw)
|
||||||
|
return _file(path,mode,*args,**kw)
|
||||||
|
|
||||||
|
def _open(self, path, mode='r', *args, **kw):
|
||||||
|
if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
|
||||||
|
self._violation("open", path, mode, *args, **kw)
|
||||||
|
return _open(path,mode,*args,**kw)
|
||||||
|
|
||||||
|
def tmpnam(self):
|
||||||
|
self._violation("tmpnam")
|
||||||
|
|
||||||
|
def _ok(self,path):
|
||||||
|
active = self._active
|
||||||
|
try:
|
||||||
|
self._active = False
|
||||||
|
realpath = os.path.normcase(os.path.realpath(path))
|
||||||
|
if (self._exempted(realpath) or realpath == self._sandbox
|
||||||
|
or realpath.startswith(self._prefix)):
|
||||||
|
return True
|
||||||
|
finally:
|
||||||
|
self._active = active
|
||||||
|
|
||||||
|
def _exempted(self, filepath):
|
||||||
|
exception_matches = map(filepath.startswith, self._exceptions)
|
||||||
|
return True in exception_matches
|
||||||
|
|
||||||
|
def _remap_input(self,operation,path,*args,**kw):
|
||||||
|
"""Called for path inputs"""
|
||||||
|
if operation in self.write_ops and not self._ok(path):
|
||||||
|
self._violation(operation, os.path.realpath(path), *args, **kw)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def _remap_pair(self,operation,src,dst,*args,**kw):
|
||||||
|
"""Called for path pairs like rename, link, and symlink operations"""
|
||||||
|
if not self._ok(src) or not self._ok(dst):
|
||||||
|
self._violation(operation, src, dst, *args, **kw)
|
||||||
|
return (src,dst)
|
||||||
|
|
||||||
|
def open(self, file, flags, mode=0777):
|
||||||
|
"""Called for low-level os.open()"""
|
||||||
|
if flags & WRITE_FLAGS and not self._ok(file):
|
||||||
|
self._violation("os.open", file, flags, mode)
|
||||||
|
return _os.open(file,flags,mode)
|
||||||
|
|
||||||
|
|
||||||
|
WRITE_FLAGS = reduce(
|
||||||
|
operator.or_,
|
||||||
|
[getattr(_os, a, 0) for a in
|
||||||
|
"O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SandboxViolation(DistutilsError):
|
||||||
|
"""A setup script attempted to modify the filesystem outside the sandbox"""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return """SandboxViolation: %s%r %s
|
||||||
|
|
||||||
|
The package setup script has attempted to modify files on your system
|
||||||
|
that are not within the EasyInstall build area, and has been aborted.
|
||||||
|
|
||||||
|
This package cannot be safely installed by EasyInstall, and may not
|
||||||
|
support alternate installation locations even if you run its setup
|
||||||
|
script by hand. Please inform the package's author and the EasyInstall
|
||||||
|
maintainers to find out if a fix or workaround is available.""" % self.args
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user