mirror of
https://github.com/kennethreitz/heroku-buildpack-python.git
synced 2026-06-05 23:10:16 +00:00
new style of pip and setuptools vendoring
This commit is contained in:
@@ -183,6 +183,8 @@ if [ "$FRESH_PYTHON" ] || [[ ! $(pip --version) == *$PIP_VERSION* ]]; then
|
||||
bpwatch start install_setuptools
|
||||
# Prepare it for the real world
|
||||
puts-step "Installing Setuptools ($SETUPTOOLS_VERSION)"
|
||||
cd $ROOT_DIR/vendor/
|
||||
tar zxf setuptools-$SETUPTOOLS_VERSION.tar.gz
|
||||
cd $ROOT_DIR/vendor/setuptools-$SETUPTOOLS_VERSION/
|
||||
python setup.py install &> /dev/null
|
||||
cd $WORKING_DIR
|
||||
@@ -190,6 +192,9 @@ if [ "$FRESH_PYTHON" ] || [[ ! $(pip --version) == *$PIP_VERSION* ]]; then
|
||||
|
||||
bpwatch start install_pip
|
||||
puts-step "Installing Pip ($PIP_VERSION)"
|
||||
|
||||
cd $ROOT_DIR/vendor/
|
||||
tar zxf pip-$PIP_VERSION.tar.gz
|
||||
cd $ROOT_DIR/vendor/pip-$PIP_VERSION/
|
||||
python setup.py install &> /dev/null
|
||||
cd $WORKING_DIR
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
-1708
File diff suppressed because it is too large
Load Diff
Vendored
-1708
File diff suppressed because it is too large
Load Diff
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
============================
|
||||
Quick notes for contributors
|
||||
============================
|
||||
|
||||
Setuptools is developed using the DVCS Mercurial.
|
||||
|
||||
Grab the code at bitbucket::
|
||||
|
||||
$ hg clone https://bitbucket.org/pypa/setuptools
|
||||
|
||||
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
|
||||
|
||||
Please commit bug-fixes against the current maintenance branch and new
|
||||
features to the default branch.
|
||||
|
||||
You can run the tests via::
|
||||
|
||||
$ python setup.py test
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
recursive-include setuptools *.py *.txt *.exe *.xml
|
||||
recursive-include tests *.py *.c *.pyx *.txt
|
||||
recursive-include setuptools/tests *.html entries*
|
||||
recursive-include setuptools/tests/svn_data *.zip
|
||||
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
-1947
File diff suppressed because it is too large
Load Diff
Vendored
-211
@@ -1,211 +0,0 @@
|
||||
===============================
|
||||
Installing and Using Setuptools
|
||||
===============================
|
||||
|
||||
.. contents:: **Table of Contents**
|
||||
|
||||
|
||||
-------------------------
|
||||
Installation Instructions
|
||||
-------------------------
|
||||
|
||||
The recommended way to bootstrap setuptools on any system is to download
|
||||
`ez_setup.py`_ and run it using the target Python environment. Different
|
||||
operating systems have different recommended techniques to accomplish this
|
||||
basic routine, so below are some examples to get you started.
|
||||
|
||||
Setuptools requires Python 2.6 or later. To install setuptools
|
||||
on Python 2.4 or Python 2.5, use the `bootstrap script for Setuptools 1.x
|
||||
<https://bitbucket.org/pypa/setuptools/raw/bootstrap-py24/ez_setup.py>`_.
|
||||
|
||||
The link provided to ez_setup.py is a bookmark to bootstrap script for the
|
||||
latest known stable release.
|
||||
|
||||
.. _ez_setup.py: https://bootstrap.pypa.io/ez_setup.py
|
||||
|
||||
Windows 8 (Powershell)
|
||||
======================
|
||||
|
||||
For best results, uninstall previous versions FIRST (see `Uninstalling`_).
|
||||
|
||||
Using Windows 8 or later, it's possible to install with one simple Powershell
|
||||
command. Start up Powershell and paste this command::
|
||||
|
||||
> (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python -
|
||||
|
||||
You must start the Powershell with Administrative privileges or you may choose
|
||||
to install a user-local installation::
|
||||
|
||||
> (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - --user
|
||||
|
||||
If you have Python 3.3 or later, you can use the ``py`` command to install to
|
||||
different Python versions. For example, to install to Python 3.3 if you have
|
||||
Python 2.7 installed::
|
||||
|
||||
> (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | py -3 -
|
||||
|
||||
The recommended way to install setuptools on Windows is to download
|
||||
`ez_setup.py`_ and run it. The script will download the appropriate .egg
|
||||
file and install it for you.
|
||||
|
||||
Once installation is complete, you will find an ``easy_install`` program in
|
||||
your Python ``Scripts`` subdirectory. For simple invocation and best results,
|
||||
add this directory to your ``PATH`` environment variable, if it is not already
|
||||
present. If you did a user-local install, the ``Scripts`` subdirectory is
|
||||
``$env:APPDATA\Python\Scripts``.
|
||||
|
||||
|
||||
Windows 7 (or graphical install)
|
||||
================================
|
||||
|
||||
For Windows 7 and earlier, download `ez_setup.py`_ using your favorite web
|
||||
browser or other technique and "run" that file.
|
||||
|
||||
|
||||
Unix (wget)
|
||||
===========
|
||||
|
||||
Most Linux distributions come with wget.
|
||||
|
||||
Download `ez_setup.py`_ and run it using the target Python version. The script
|
||||
will download the appropriate version and install it for you::
|
||||
|
||||
> wget https://bootstrap.pypa.io/ez_setup.py -O - | python
|
||||
|
||||
Note that you will may need to invoke the command with superuser privileges to
|
||||
install to the system Python::
|
||||
|
||||
> wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python
|
||||
|
||||
Alternatively, Setuptools may be installed to a user-local path::
|
||||
|
||||
> wget https://bootstrap.pypa.io/ez_setup.py -O - | python - --user
|
||||
|
||||
Unix including Mac OS X (curl)
|
||||
==============================
|
||||
|
||||
If your system has curl installed, follow the ``wget`` instructions but
|
||||
replace ``wget`` with ``curl`` and ``-O`` with ``-o``. For example::
|
||||
|
||||
> curl https://bootstrap.pypa.io/ez_setup.py -o - | python
|
||||
|
||||
|
||||
Advanced Installation
|
||||
=====================
|
||||
|
||||
For more advanced installation options, such as installing to custom
|
||||
locations or prefixes, download and extract the source
|
||||
tarball from `Setuptools on PyPI <https://pypi.python.org/pypi/setuptools>`_
|
||||
and run setup.py with any supported distutils and Setuptools options.
|
||||
For example::
|
||||
|
||||
setuptools-x.x$ python setup.py install --prefix=/opt/setuptools
|
||||
|
||||
Use ``--help`` to get a full options list, but we recommend consulting
|
||||
the `EasyInstall manual`_ for detailed instructions, especially `the section
|
||||
on custom installation locations`_.
|
||||
|
||||
.. _EasyInstall manual: https://pythonhosted.org/setuptools/EasyInstall
|
||||
.. _the section on custom installation locations: https://pythonhosted.org/setuptools/EasyInstall#custom-installation-locations
|
||||
|
||||
|
||||
Downloads
|
||||
=========
|
||||
|
||||
All setuptools downloads can be found at `the project's home page in the Python
|
||||
Package Index`_. Scroll to the very bottom of the page to find the links.
|
||||
|
||||
.. _the project's home page in the Python Package Index: https://pypi.python.org/pypi/setuptools
|
||||
|
||||
In addition to the PyPI downloads, the development version of ``setuptools``
|
||||
is available from the `Bitbucket repo`_, and in-development versions of the
|
||||
`0.6 branch`_ are available as well.
|
||||
|
||||
.. _Bitbucket repo: https://bitbucket.org/pypa/setuptools/get/default.tar.gz#egg=setuptools-dev
|
||||
.. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06
|
||||
|
||||
Uninstalling
|
||||
============
|
||||
|
||||
On Windows, if Setuptools was installed using an ``.exe`` or ``.msi``
|
||||
installer, simply use the uninstall feature of "Add/Remove Programs" in the
|
||||
Control Panel.
|
||||
|
||||
Otherwise, to uninstall Setuptools or Distribute, regardless of the Python
|
||||
version, delete all ``setuptools*`` and ``distribute*`` files and
|
||||
directories from your system's ``site-packages`` directory
|
||||
(and any other ``sys.path`` directories) FIRST.
|
||||
|
||||
If you are upgrading or otherwise plan to re-install Setuptools or Distribute,
|
||||
nothing further needs to be done. If you want to completely remove Setuptools,
|
||||
you may also want to remove the 'easy_install' and 'easy_install-x.x' scripts
|
||||
and associated executables installed to the Python scripts directory.
|
||||
|
||||
--------------------------------
|
||||
Using Setuptools and EasyInstall
|
||||
--------------------------------
|
||||
|
||||
Here are some of the available manuals, tutorials, and other resources for
|
||||
learning about Setuptools, Python Eggs, and EasyInstall:
|
||||
|
||||
* `The EasyInstall user's guide and reference manual`_
|
||||
* `The setuptools Developer's Guide`_
|
||||
* `The pkg_resources API reference`_
|
||||
* `Package Compatibility Notes`_ (user-maintained)
|
||||
* `The Internal Structure of Python Eggs`_
|
||||
|
||||
Questions, comments, and bug reports should be directed to the `distutils-sig
|
||||
mailing list`_. If you have written (or know of) any tutorials, documentation,
|
||||
plug-ins, or other resources for setuptools users, please let us know about
|
||||
them there, so this reference list can be updated. If you have working,
|
||||
*tested* patches to correct problems or add features, you may submit them to
|
||||
the `setuptools bug tracker`_.
|
||||
|
||||
.. _setuptools bug tracker: https://bitbucket.org/pypa/setuptools/issues
|
||||
.. _Package Compatibility Notes: https://pythonhosted.org/setuptools/PackageNotes
|
||||
.. _The Internal Structure of Python Eggs: https://pythonhosted.org/setuptools/formats.html
|
||||
.. _The setuptools Developer's Guide: https://pythonhosted.org/setuptools/setuptools.html
|
||||
.. _The pkg_resources API reference: https://pythonhosted.org/setuptools/pkg_resources.html
|
||||
.. _The EasyInstall user's guide and reference manual: https://pythonhosted.org/setuptools/easy_install.html
|
||||
.. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
|
||||
|
||||
|
||||
-------
|
||||
Credits
|
||||
-------
|
||||
|
||||
* The original design for the ``.egg`` format and the ``pkg_resources`` API was
|
||||
co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first
|
||||
version of ``pkg_resources``, and supplied the OS X operating system version
|
||||
compatibility algorithm.
|
||||
|
||||
* Ian Bicking implemented many early "creature comfort" features of
|
||||
easy_install, including support for downloading via Sourceforge and
|
||||
Subversion repositories. Ian's comments on the Web-SIG about WSGI
|
||||
application deployment also inspired the concept of "entry points" in eggs,
|
||||
and he has given talks at PyCon and elsewhere to inform and educate the
|
||||
community about eggs and setuptools.
|
||||
|
||||
* Jim Fulton contributed time and effort to build automated tests of various
|
||||
aspects of ``easy_install``, and supplied the doctests for the command-line
|
||||
``.exe`` wrappers on Windows.
|
||||
|
||||
* Phillip J. Eby is the seminal author of setuptools, and
|
||||
first proposed the idea of an importable binary distribution format for
|
||||
Python application plug-ins.
|
||||
|
||||
* Significant parts of the implementation of setuptools were funded by the Open
|
||||
Source Applications Foundation, to provide a plug-in infrastructure for the
|
||||
Chandler PIM application. In addition, many OSAF staffers (such as Mike
|
||||
"Code Bear" Taylor) contributed their time and stress as guinea pigs for the
|
||||
use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!)
|
||||
|
||||
* Tarek Ziadé is the principal author of the Distribute fork, which
|
||||
re-invigorated the community on the project, encouraged renewed innovation,
|
||||
and addressed many defects.
|
||||
|
||||
* Since the merge with Distribute, Jason R. Coombs is the
|
||||
maintainer of setuptools. The project is maintained in coordination with
|
||||
the Python Packaging Authority (PyPA) and the larger Python community.
|
||||
|
||||
.. _files:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
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)()
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
# -*- 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
|
||||
}
|
||||
|
||||
for var in list(_VARS.keys()):
|
||||
if '.' in var:
|
||||
_VARS[var.replace('.', '_')] = _VARS[var]
|
||||
|
||||
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)
|
||||
Vendored
-75
@@ -1,75 +0,0 @@
|
||||
# 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."
|
||||
@@ -1,8 +0,0 @@
|
||||
<h3>Download</h3>
|
||||
|
||||
<p>Current version: <b>{{ version }}</b></p>
|
||||
<p>Get Setuptools from the <a href="https://pypi.python.org/pypi/setuptools"> Python Package Index</a>
|
||||
|
||||
<h3>Questions? Suggestions? Contributions?</h3>
|
||||
|
||||
<p>Visit the <a href="https://bitbucket.org/pypa/setuptools">Setuptools project page</a> </p>
|
||||
@@ -1,237 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
.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 */
|
||||
@@ -1,4 +0,0 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = nature.css
|
||||
pygments_style = tango
|
||||
Vendored
-197
@@ -1,197 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Setuptools 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 setup as setup_script
|
||||
|
||||
# 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 = 'Setuptools'
|
||||
copyright = '2009-2013, 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 = setup_script.setup_params['version']
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = setup_script.setup_params['version']
|
||||
|
||||
# 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 = "Setuptools documentation"
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
html_short_title = "Setuptools"
|
||||
|
||||
# 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 = 'Setuptoolsdoc'
|
||||
|
||||
|
||||
# -- 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', 'Setuptools.tex', 'Setuptools Documentation',
|
||||
'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
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
-------------------------
|
||||
Development on Setuptools
|
||||
-------------------------
|
||||
|
||||
Setuptools is maintained by the Python community under the Python Packaging
|
||||
Authority (PyPA) and led by Jason R. Coombs.
|
||||
|
||||
This document describes the process by which Setuptools is developed.
|
||||
This document assumes the reader has some passing familiarity with
|
||||
*using* setuptools, the ``pkg_resources`` module, and EasyInstall. It
|
||||
does not attempt to explain basic concepts like inter-project
|
||||
dependencies, nor does it contain detailed lexical syntax for most
|
||||
file formats. Neither does it explain concepts like "namespace
|
||||
packages" or "resources" in any detail, as all of these subjects are
|
||||
covered at length in the setuptools developer's guide and the
|
||||
``pkg_resources`` reference manual.
|
||||
|
||||
Instead, this is **internal** documentation for how those concepts and
|
||||
features are *implemented* in concrete terms. It is intended for people
|
||||
who are working on the setuptools code base, who want to be able to
|
||||
troubleshoot setuptools problems, want to write code that reads the file
|
||||
formats involved, or want to otherwise tinker with setuptools-generated
|
||||
files and directories.
|
||||
|
||||
Note, however, that these are all internal implementation details and
|
||||
are therefore subject to change; stick to the published API if you don't
|
||||
want to be responsible for keeping your code from breaking when
|
||||
setuptools changes. You have been warned.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
formats
|
||||
releases
|
||||
|
||||
-1625
File diff suppressed because it is too large
Load Diff
-676
@@ -1,676 +0,0 @@
|
||||
=====================================
|
||||
The Internal Structure of Python Eggs
|
||||
=====================================
|
||||
|
||||
STOP! This is not the first document you should read!
|
||||
|
||||
|
||||
|
||||
.. contents:: **Table of Contents**
|
||||
|
||||
|
||||
----------------------
|
||||
Eggs and their Formats
|
||||
----------------------
|
||||
|
||||
A "Python egg" is a logical structure embodying the release of a
|
||||
specific version of a Python project, comprising its code, resources,
|
||||
and metadata. There are multiple formats that can be used to physically
|
||||
encode a Python egg, and others can be developed. However, a key
|
||||
principle of Python eggs is that they should be discoverable and
|
||||
importable. That is, it should be possible for a Python application to
|
||||
easily and efficiently find out what eggs are present on a system, and
|
||||
to ensure that the desired eggs' contents are importable.
|
||||
|
||||
There are two basic formats currently implemented for Python eggs:
|
||||
|
||||
1. ``.egg`` format: a directory or zipfile *containing* the project's
|
||||
code and resources, along with an ``EGG-INFO`` subdirectory that
|
||||
contains the project's metadata
|
||||
|
||||
2. ``.egg-info`` format: a file or directory placed *adjacent* to the
|
||||
project's code and resources, that directly contains the project's
|
||||
metadata.
|
||||
|
||||
Both formats can include arbitrary Python code and resources, including
|
||||
static data files, package and non-package directories, Python
|
||||
modules, C extension modules, and so on. But each format is optimized
|
||||
for different purposes.
|
||||
|
||||
The ``.egg`` format is well-suited to distribution and the easy
|
||||
uninstallation or upgrades of code, since the project is essentially
|
||||
self-contained within a single directory or file, unmingled with any
|
||||
other projects' code or resources. It also makes it possible to have
|
||||
multiple versions of a project simultaneously installed, such that
|
||||
individual programs can select the versions they wish to use.
|
||||
|
||||
The ``.egg-info`` format, on the other hand, was created to support
|
||||
backward-compatibility, performance, and ease of installation for system
|
||||
packaging tools that expect to install all projects' code and resources
|
||||
to a single directory (e.g. ``site-packages``). Placing the metadata
|
||||
in that same directory simplifies the installation process, since it
|
||||
isn't necessary to create ``.pth`` files or otherwise modify
|
||||
``sys.path`` to include each installed egg.
|
||||
|
||||
Its disadvantage, however, is that it provides no support for clean
|
||||
uninstallation or upgrades, and of course only a single version of a
|
||||
project can be installed to a given directory. Thus, support from a
|
||||
package management tool is required. (This is why setuptools' "install"
|
||||
command refers to this type of egg installation as "single-version,
|
||||
externally managed".) Also, they lack sufficient data to allow them to
|
||||
be copied from their installation source. easy_install can "ship" an
|
||||
application by copying ``.egg`` files or directories to a target
|
||||
location, but it cannot do this for ``.egg-info`` installs, because
|
||||
there is no way to tell what code and resources belong to a particular
|
||||
egg -- there may be several eggs "scrambled" together in a single
|
||||
installation location, and the ``.egg-info`` format does not currently
|
||||
include a way to list the files that were installed. (This may change
|
||||
in a future version.)
|
||||
|
||||
|
||||
Code and Resources
|
||||
==================
|
||||
|
||||
The layout of the code and resources is dictated by Python's normal
|
||||
import layout, relative to the egg's "base location".
|
||||
|
||||
For the ``.egg`` format, the base location is the ``.egg`` itself. That
|
||||
is, adding the ``.egg`` filename or directory name to ``sys.path``
|
||||
makes its contents importable.
|
||||
|
||||
For the ``.egg-info`` format, however, the base location is the
|
||||
directory that *contains* the ``.egg-info``, and thus it is the
|
||||
directory that must be added to ``sys.path`` to make the egg importable.
|
||||
(Note that this means that the "normal" installation of a package to a
|
||||
``sys.path`` directory is sufficient to make it an "egg" if it has an
|
||||
``.egg-info`` file or directory installed alongside of it.)
|
||||
|
||||
|
||||
Project Metadata
|
||||
=================
|
||||
|
||||
If eggs contained only code and resources, there would of course be
|
||||
no difference between them and any other directory or zip file on
|
||||
``sys.path``. Thus, metadata must also be included, using a metadata
|
||||
file or directory.
|
||||
|
||||
For the ``.egg`` format, the metadata is placed in an ``EGG-INFO``
|
||||
subdirectory, directly within the ``.egg`` file or directory. For the
|
||||
``.egg-info`` format, metadata is stored directly within the
|
||||
``.egg-info`` directory itself.
|
||||
|
||||
The minimum project metadata that all eggs must have is a standard
|
||||
Python ``PKG-INFO`` file, named ``PKG-INFO`` and placed within the
|
||||
metadata directory appropriate to the format. Because it's possible for
|
||||
this to be the only metadata file included, ``.egg-info`` format eggs
|
||||
are not required to be a directory; they can just be a ``.egg-info``
|
||||
file that directly contains the ``PKG-INFO`` metadata. This eliminates
|
||||
the need to create a directory just to store one file. This option is
|
||||
*not* available for ``.egg`` formats, since setuptools always includes
|
||||
other metadata. (In fact, setuptools itself never generates
|
||||
``.egg-info`` files, either; the support for using files was added so
|
||||
that the requirement could easily be satisfied by other tools, such
|
||||
as the distutils in Python 2.5).
|
||||
|
||||
In addition to the ``PKG-INFO`` file, an egg's metadata directory may
|
||||
also include files and directories representing various forms of
|
||||
optional standard metadata (see the section on `Standard Metadata`_,
|
||||
below) or user-defined metadata required by the project. For example,
|
||||
some projects may define a metadata format to describe their application
|
||||
plugins, and metadata in this format would then be included by plugin
|
||||
creators in their projects' metadata directories.
|
||||
|
||||
|
||||
Filename-Embedded Metadata
|
||||
==========================
|
||||
|
||||
To allow introspection of installed projects and runtime resolution of
|
||||
inter-project dependencies, a certain amount of information is embedded
|
||||
in egg filenames. At a minimum, this includes the project name, and
|
||||
ideally will also include the project version number. Optionally, it
|
||||
can also include the target Python version and required runtime
|
||||
platform if platform-specific C code is included. The syntax of an
|
||||
egg filename is as follows::
|
||||
|
||||
name ["-" version ["-py" pyver ["-" required_platform]]] "." ext
|
||||
|
||||
The "name" and "version" should be escaped using the ``to_filename()``
|
||||
function provided by ``pkg_resources``, after first processing them with
|
||||
``safe_name()`` and ``safe_version()`` respectively. These latter two
|
||||
functions can also be used to later "unescape" these parts of the
|
||||
filename. (For a detailed description of these transformations, please
|
||||
see the "Parsing Utilities" section of the ``pkg_resources`` manual.)
|
||||
|
||||
The "pyver" string is the Python major version, as found in the first
|
||||
3 characters of ``sys.version``. "required_platform" is essentially
|
||||
a distutils ``get_platform()`` string, but with enhancements to properly
|
||||
distinguish Mac OS versions. (See the ``get_build_platform()``
|
||||
documentation in the "Platform Utilities" section of the
|
||||
``pkg_resources`` manual for more details.)
|
||||
|
||||
Finally, the "ext" is either ``.egg`` or ``.egg-info``, as appropriate
|
||||
for the egg's format.
|
||||
|
||||
Normally, an egg's filename should include at least the project name and
|
||||
version, as this allows the runtime system to find desired project
|
||||
versions without having to read the egg's PKG-INFO to determine its
|
||||
version number.
|
||||
|
||||
Setuptools, however, only includes the version number in the filename
|
||||
when an ``.egg`` file is built using the ``bdist_egg`` command, or when
|
||||
an ``.egg-info`` directory is being installed by the
|
||||
``install_egg_info`` command. When generating metadata for use with the
|
||||
original source tree, it only includes the project name, so that the
|
||||
directory will not have to be renamed each time the project's version
|
||||
changes.
|
||||
|
||||
This is especially important when version numbers change frequently, and
|
||||
the source metadata directory is kept under version control with the
|
||||
rest of the project. (As would be the case when the project's source
|
||||
includes project-defined metadata that is not generated from by
|
||||
setuptools from data in the setup script.)
|
||||
|
||||
|
||||
Egg Links
|
||||
=========
|
||||
|
||||
In addition to the ``.egg`` and ``.egg-info`` formats, there is a third
|
||||
egg-related extension that you may encounter on occasion: ``.egg-link``
|
||||
files.
|
||||
|
||||
These files are not eggs, strictly speaking. They simply provide a way
|
||||
to reference an egg that is not physically installed in the desired
|
||||
location. They exist primarily as a cross-platform alternative to
|
||||
symbolic links, to support "installing" code that is being developed in
|
||||
a different location than the desired installation location. For
|
||||
example, if a user is developing an application plugin in their home
|
||||
directory, but the plugin needs to be "installed" in an application
|
||||
plugin directory, running "setup.py develop -md /path/to/app/plugins"
|
||||
will install an ``.egg-link`` file in ``/path/to/app/plugins``, that
|
||||
tells the egg runtime system where to find the actual egg (the user's
|
||||
project source directory and its ``.egg-info`` subdirectory).
|
||||
|
||||
``.egg-link`` files are named following the format for ``.egg`` and
|
||||
``.egg-info`` names, but only the project name is included; no version,
|
||||
Python version, or platform information is included. When the runtime
|
||||
searches for available eggs, ``.egg-link`` files are opened and the
|
||||
actual egg file/directory name is read from them.
|
||||
|
||||
Each ``.egg-link`` file should contain a single file or directory name,
|
||||
with no newlines. This filename should be the base location of one or
|
||||
more eggs. That is, the name must either end in ``.egg``, or else it
|
||||
should be the parent directory of one or more ``.egg-info`` format eggs.
|
||||
|
||||
As of setuptools 0.6c6, the path may be specified as a platform-independent
|
||||
(i.e. ``/``-separated) relative path from the directory containing the
|
||||
``.egg-link`` file, and a second line may appear in the file, specifying a
|
||||
platform-independent relative path from the egg's base directory to its
|
||||
setup script directory. This allows installation tools such as EasyInstall
|
||||
to find the project's setup directory and build eggs or perform other setup
|
||||
commands on it.
|
||||
|
||||
|
||||
-----------------
|
||||
Standard Metadata
|
||||
-----------------
|
||||
|
||||
In addition to the minimum required ``PKG-INFO`` metadata, projects can
|
||||
include a variety of standard metadata files or directories, as
|
||||
described below. Except as otherwise noted, these files and directories
|
||||
are automatically generated by setuptools, based on information supplied
|
||||
in the setup script or through analysis of the project's code and
|
||||
resources.
|
||||
|
||||
Most of these files and directories are generated via "egg-info
|
||||
writers" during execution of the setuptools ``egg_info`` command, and
|
||||
are listed in the ``egg_info.writers`` entry point group defined by
|
||||
setuptools' own ``setup.py`` file.
|
||||
|
||||
Project authors can register their own metadata writers as entry points
|
||||
in this group (as described in the setuptools manual under "Adding new
|
||||
EGG-INFO Files") to cause setuptools to generate project-specific
|
||||
metadata files or directories during execution of the ``egg_info``
|
||||
command. It is up to project authors to document these new metadata
|
||||
formats, if they create any.
|
||||
|
||||
|
||||
``.txt`` File Formats
|
||||
=====================
|
||||
|
||||
Files described in this section that have ``.txt`` extensions have a
|
||||
simple lexical format consisting of a sequence of text lines, each line
|
||||
terminated by a linefeed character (regardless of platform). Leading
|
||||
and trailing whitespace on each line is ignored, as are blank lines and
|
||||
lines whose first nonblank character is a ``#`` (comment symbol). (This
|
||||
is the parsing format defined by the ``yield_lines()`` function of
|
||||
the ``pkg_resources`` module.)
|
||||
|
||||
All ``.txt`` files defined by this section follow this format, but some
|
||||
are also "sectioned" files, meaning that their contents are divided into
|
||||
sections, using square-bracketed section headers akin to Windows
|
||||
``.ini`` format. Note that this does *not* imply that the lines within
|
||||
the sections follow an ``.ini`` format, however. Please see an
|
||||
individual metadata file's documentation for a description of what the
|
||||
lines and section names mean in that particular file.
|
||||
|
||||
Sectioned files can be parsed using the ``split_sections()`` function;
|
||||
see the "Parsing Utilities" section of the ``pkg_resources`` manual for
|
||||
for details.
|
||||
|
||||
|
||||
Dependency Metadata
|
||||
===================
|
||||
|
||||
|
||||
``requires.txt``
|
||||
----------------
|
||||
|
||||
This is a "sectioned" text file. Each section is a sequence of
|
||||
"requirements", as parsed by the ``parse_requirements()`` function;
|
||||
please see the ``pkg_resources`` manual for the complete requirement
|
||||
parsing syntax.
|
||||
|
||||
The first, unnamed section (i.e., before the first section header) in
|
||||
this file is the project's core requirements, which must be installed
|
||||
for the project to function. (Specified using the ``install_requires``
|
||||
keyword to ``setup()``).
|
||||
|
||||
The remaining (named) sections describe the project's "extra"
|
||||
requirements, as specified using the ``extras_require`` keyword to
|
||||
``setup()``. The section name is the name of the optional feature, and
|
||||
the section body lists that feature's dependencies.
|
||||
|
||||
Note that it is not normally necessary to inspect this file directly;
|
||||
``pkg_resources.Distribution`` objects have a ``requires()`` method
|
||||
that can be used to obtain ``Requirement`` objects describing the
|
||||
project's core and optional dependencies.
|
||||
|
||||
|
||||
|
||||
``dependency_links.txt``
|
||||
------------------------
|
||||
|
||||
A list of dependency URLs, one per line, as specified using the
|
||||
``dependency_links`` keyword to ``setup()``. These may be direct
|
||||
download URLs, or the URLs of web pages containing direct download
|
||||
links, and will be used by EasyInstall to find dependencies, as though
|
||||
the user had manually provided them via the ``--find-links`` command
|
||||
line option. Please see the setuptools manual and EasyInstall manual
|
||||
for more information on specifying this option, and for information on
|
||||
how EasyInstall processes ``--find-links`` URLs.
|
||||
|
||||
|
||||
``depends.txt`` -- Obsolete, do not create!
|
||||
-------------------------------------------
|
||||
|
||||
This file follows an identical format to ``requires.txt``, but is
|
||||
obsolete and should not be used. The earliest versions of setuptools
|
||||
required users to manually create and maintain this file, so the runtime
|
||||
still supports reading it, if it exists. The new filename was created
|
||||
so that it could be automatically generated from ``setup()`` information
|
||||
without overwriting an existing hand-created ``depends.txt``, if one
|
||||
was already present in the project's source ``.egg-info`` directory.
|
||||
|
||||
|
||||
``namespace_packages.txt`` -- Namespace Package Metadata
|
||||
========================================================
|
||||
|
||||
A list of namespace package names, one per line, as supplied to the
|
||||
``namespace_packages`` keyword to ``setup()``. Please see the manuals
|
||||
for setuptools and ``pkg_resources`` for more information about
|
||||
namespace packages.
|
||||
|
||||
|
||||
``entry_points.txt`` -- "Entry Point"/Plugin Metadata
|
||||
=====================================================
|
||||
|
||||
This is a "sectioned" text file, whose contents encode the
|
||||
``entry_points`` keyword supplied to ``setup()``. All sections are
|
||||
named, as the section names specify the entry point groups in which the
|
||||
corresponding section's entry points are registered.
|
||||
|
||||
Each section is a sequence of "entry point" lines, each parseable using
|
||||
the ``EntryPoint.parse`` classmethod; please see the ``pkg_resources``
|
||||
manual for the complete entry point parsing syntax.
|
||||
|
||||
Note that it is not necessary to parse this file directly; the
|
||||
``pkg_resources`` module provides a variety of APIs to locate and load
|
||||
entry points automatically. Please see the setuptools and
|
||||
``pkg_resources`` manuals for details on the nature and uses of entry
|
||||
points.
|
||||
|
||||
|
||||
The ``scripts`` Subdirectory
|
||||
============================
|
||||
|
||||
This directory is currently only created for ``.egg`` files built by
|
||||
the setuptools ``bdist_egg`` command. It will contain copies of all
|
||||
of the project's "traditional" scripts (i.e., those specified using the
|
||||
``scripts`` keyword to ``setup()``). This is so that they can be
|
||||
reconstituted when an ``.egg`` file is installed.
|
||||
|
||||
The scripts are placed here using the disutils' standard
|
||||
``install_scripts`` command, so any ``#!`` lines reflect the Python
|
||||
installation where the egg was built. But instead of copying the
|
||||
scripts to the local script installation directory, EasyInstall writes
|
||||
short wrapper scripts that invoke the original scripts from inside the
|
||||
egg, after ensuring that sys.path includes the egg and any eggs it
|
||||
depends on. For more about `script wrappers`_, see the section below on
|
||||
`Installation and Path Management Issues`_.
|
||||
|
||||
|
||||
Zip Support Metadata
|
||||
====================
|
||||
|
||||
|
||||
``native_libs.txt``
|
||||
-------------------
|
||||
|
||||
A list of C extensions and other dynamic link libraries contained in
|
||||
the egg, one per line. Paths are ``/``-separated and relative to the
|
||||
egg's base location.
|
||||
|
||||
This file is generated as part of ``bdist_egg`` processing, and as such
|
||||
only appears in ``.egg`` files (and ``.egg`` directories created by
|
||||
unpacking them). It is used to ensure that all libraries are extracted
|
||||
from a zipped egg at the same time, in case there is any direct linkage
|
||||
between them. Please see the `Zip File Issues`_ section below for more
|
||||
information on library and resource extraction from ``.egg`` files.
|
||||
|
||||
|
||||
``eager_resources.txt``
|
||||
-----------------------
|
||||
|
||||
A list of resource files and/or directories, one per line, as specified
|
||||
via the ``eager_resources`` keyword to ``setup()``. Paths are
|
||||
``/``-separated and relative to the egg's base location.
|
||||
|
||||
Resource files or directories listed here will be extracted
|
||||
simultaneously, if any of the named resources are extracted, or if any
|
||||
native libraries listed in ``native_libs.txt`` are extracted. Please
|
||||
see the setuptools manual for details on what this feature is used for
|
||||
and how it works, as well as the `Zip File Issues`_ section below.
|
||||
|
||||
|
||||
``zip-safe`` and ``not-zip-safe``
|
||||
---------------------------------
|
||||
|
||||
These are zero-length files, and either one or the other should exist.
|
||||
If ``zip-safe`` exists, it means that the project will work properly
|
||||
when installed as an ``.egg`` zipfile, and conversely the existence of
|
||||
``not-zip-safe`` means the project should not be installed as an
|
||||
``.egg`` file. The ``zip_safe`` option to setuptools' ``setup()``
|
||||
determines which file will be written. If the option isn't provided,
|
||||
setuptools attempts to make its own assessment of whether the package
|
||||
can work, based on code and content analysis.
|
||||
|
||||
If neither file is present at installation time, EasyInstall defaults
|
||||
to assuming that the project should be unzipped. (Command-line options
|
||||
to EasyInstall, however, take precedence even over an existing
|
||||
``zip-safe`` or ``not-zip-safe`` file.)
|
||||
|
||||
Note that these flag files appear only in ``.egg`` files generated by
|
||||
``bdist_egg``, and in ``.egg`` directories created by unpacking such an
|
||||
``.egg`` file.
|
||||
|
||||
|
||||
|
||||
``top_level.txt`` -- Conflict Management Metadata
|
||||
=================================================
|
||||
|
||||
This file is a list of the top-level module or package names provided
|
||||
by the project, one Python identifier per line.
|
||||
|
||||
Subpackages are not included; a project containing both a ``foo.bar``
|
||||
and a ``foo.baz`` would include only one line, ``foo``, in its
|
||||
``top_level.txt``.
|
||||
|
||||
This data is used by ``pkg_resources`` at runtime to issue a warning if
|
||||
an egg is added to ``sys.path`` when its contained packages may have
|
||||
already been imported.
|
||||
|
||||
(It was also once used to detect conflicts with non-egg packages at
|
||||
installation time, but in more recent versions, setuptools installs eggs
|
||||
in such a way that they always override non-egg packages, thus
|
||||
preventing a problem from arising.)
|
||||
|
||||
|
||||
``SOURCES.txt`` -- Source Files Manifest
|
||||
========================================
|
||||
|
||||
This file is roughly equivalent to the distutils' ``MANIFEST`` file.
|
||||
The differences are as follows:
|
||||
|
||||
* The filenames always use ``/`` as a path separator, which must be
|
||||
converted back to a platform-specific path whenever they are read.
|
||||
|
||||
* The file is automatically generated by setuptools whenever the
|
||||
``egg_info`` or ``sdist`` commands are run, and it is *not*
|
||||
user-editable.
|
||||
|
||||
Although this metadata is included with distributed eggs, it is not
|
||||
actually used at runtime for any purpose. Its function is to ensure
|
||||
that setuptools-built *source* distributions can correctly discover
|
||||
what files are part of the project's source, even if the list had been
|
||||
generated using revision control metadata on the original author's
|
||||
system.
|
||||
|
||||
In other words, ``SOURCES.txt`` has little or no runtime value for being
|
||||
included in distributed eggs, and it is possible that future versions of
|
||||
the ``bdist_egg`` and ``install_egg_info`` commands will strip it before
|
||||
installation or distribution. Therefore, do not rely on its being
|
||||
available outside of an original source directory or source
|
||||
distribution.
|
||||
|
||||
|
||||
------------------------------
|
||||
Other Technical Considerations
|
||||
------------------------------
|
||||
|
||||
|
||||
Zip File Issues
|
||||
===============
|
||||
|
||||
Although zip files resemble directories, they are not fully
|
||||
substitutable for them. Most platforms do not support loading dynamic
|
||||
link libraries contained in zipfiles, so it is not possible to directly
|
||||
import C extensions from ``.egg`` zipfiles. Similarly, there are many
|
||||
existing libraries -- whether in Python or C -- that require actual
|
||||
operating system filenames, and do not work with arbitrary "file-like"
|
||||
objects or in-memory strings, and thus cannot operate directly on the
|
||||
contents of zip files.
|
||||
|
||||
To address these issues, the ``pkg_resources`` module provides a
|
||||
"resource API" to support obtaining either the contents of a resource,
|
||||
or a true operating system filename for the resource. If the egg
|
||||
containing the resource is a directory, the resource's real filename
|
||||
is simply returned. However, if the egg is a zipfile, then the
|
||||
resource is first extracted to a cache directory, and the filename
|
||||
within the cache is returned.
|
||||
|
||||
The cache directory is determined by the ``pkg_resources`` API; please
|
||||
see the ``set_cache_path()`` and ``get_default_cache()`` documentation
|
||||
for details.
|
||||
|
||||
|
||||
The Extraction Process
|
||||
----------------------
|
||||
|
||||
Resources are extracted to a cache subdirectory whose name is based
|
||||
on the enclosing ``.egg`` filename and the path to the resource. If
|
||||
there is already a file of the correct name, size, and timestamp, its
|
||||
filename is returned to the requester. Otherwise, the desired file is
|
||||
extracted first to a temporary name generated using
|
||||
``mkstemp(".$extract",target_dir)``, and then its timestamp is set to
|
||||
match the one in the zip file, before renaming it to its final name.
|
||||
(Some collision detection and resolution code is used to handle the
|
||||
fact that Windows doesn't overwrite files when renaming.)
|
||||
|
||||
If a resource directory is requested, all of its contents are
|
||||
recursively extracted in this fashion, to ensure that the directory
|
||||
name can be used as if it were valid all along.
|
||||
|
||||
If the resource requested for extraction is listed in the
|
||||
``native_libs.txt`` or ``eager_resources.txt`` metadata files, then
|
||||
*all* resources listed in *either* file will be extracted before the
|
||||
requested resource's filename is returned, thus ensuring that all
|
||||
C extensions and data used by them will be simultaneously available.
|
||||
|
||||
|
||||
Extension Import Wrappers
|
||||
-------------------------
|
||||
|
||||
Since Python's built-in zip import feature does not support loading
|
||||
C extension modules from zipfiles, the setuptools ``bdist_egg`` command
|
||||
generates special import wrappers to make it work.
|
||||
|
||||
The wrappers are ``.py`` files (along with corresponding ``.pyc``
|
||||
and/or ``.pyo`` files) that have the same module name as the
|
||||
corresponding C extension. These wrappers are located in the same
|
||||
package directory (or top-level directory) within the zipfile, so that
|
||||
say, ``foomodule.so`` will get a corresponding ``foo.py``, while
|
||||
``bar/baz.pyd`` will get a corresponding ``bar/baz.py``.
|
||||
|
||||
These wrapper files contain a short stanza of Python code that asks
|
||||
``pkg_resources`` for the filename of the corresponding C extension,
|
||||
then reloads the module using the obtained filename. This will cause
|
||||
``pkg_resources`` to first ensure that all of the egg's C extensions
|
||||
(and any accompanying "eager resources") are extracted to the cache
|
||||
before attempting to link to the C library.
|
||||
|
||||
Note, by the way, that ``.egg`` directories will also contain these
|
||||
wrapper files. However, Python's default import priority is such that
|
||||
C extensions take precedence over same-named Python modules, so the
|
||||
import wrappers are ignored unless the egg is a zipfile.
|
||||
|
||||
|
||||
Installation and Path Management Issues
|
||||
=======================================
|
||||
|
||||
Python's initial setup of ``sys.path`` is very dependent on the Python
|
||||
version and installation platform, as well as how Python was started
|
||||
(i.e., script vs. ``-c`` vs. ``-m`` vs. interactive interpreter).
|
||||
In fact, Python also provides only two relatively robust ways to affect
|
||||
``sys.path`` outside of direct manipulation in code: the ``PYTHONPATH``
|
||||
environment variable, and ``.pth`` files.
|
||||
|
||||
However, with no cross-platform way to safely and persistently change
|
||||
environment variables, this leaves ``.pth`` files as EasyInstall's only
|
||||
real option for persistent configuration of ``sys.path``.
|
||||
|
||||
But ``.pth`` files are rather strictly limited in what they are allowed
|
||||
to do normally. They add directories only to the *end* of ``sys.path``,
|
||||
after any locally-installed ``site-packages`` directory, and they are
|
||||
only processed *in* the ``site-packages`` directory to start with.
|
||||
|
||||
This is a double whammy for users who lack write access to that
|
||||
directory, because they can't create a ``.pth`` file that Python will
|
||||
read, and even if a sympathetic system administrator adds one for them
|
||||
that calls ``site.addsitedir()`` to allow some other directory to
|
||||
contain ``.pth`` files, they won't be able to install newer versions of
|
||||
anything that's installed in the systemwide ``site-packages``, because
|
||||
their paths will still be added *after* ``site-packages``.
|
||||
|
||||
So EasyInstall applies two workarounds to solve these problems.
|
||||
|
||||
The first is that EasyInstall leverages ``.pth`` files' "import" feature
|
||||
to manipulate ``sys.path`` and ensure that anything EasyInstall adds
|
||||
to a ``.pth`` file will always appear before both the standard library
|
||||
and the local ``site-packages`` directories. Thus, it is always
|
||||
possible for a user who can write a Python-read ``.pth`` file to ensure
|
||||
that their packages come first in their own environment.
|
||||
|
||||
Second, when installing to a ``PYTHONPATH`` directory (as opposed to
|
||||
a "site" directory like ``site-packages``) EasyInstall will also install
|
||||
a special version of the ``site`` module. Because it's in a
|
||||
``PYTHONPATH`` directory, this module will get control before the
|
||||
standard library version of ``site`` does. It will record the state of
|
||||
``sys.path`` before invoking the "real" ``site`` module, and then
|
||||
afterwards it processes any ``.pth`` files found in ``PYTHONPATH``
|
||||
directories, including all the fixups needed to ensure that eggs always
|
||||
appear before the standard library in sys.path, but are in a relative
|
||||
order to one another that is defined by their ``PYTHONPATH`` and
|
||||
``.pth``-prescribed sequence.
|
||||
|
||||
The net result of these changes is that ``sys.path`` order will be
|
||||
as follows at runtime:
|
||||
|
||||
1. The ``sys.argv[0]`` directory, or an emtpy string if no script
|
||||
is being executed.
|
||||
|
||||
2. All eggs installed by EasyInstall in any ``.pth`` file in each
|
||||
``PYTHONPATH`` directory, in order first by ``PYTHONPATH`` order,
|
||||
then normal ``.pth`` processing order (which is to say alphabetical
|
||||
by ``.pth`` filename, then by the order of listing within each
|
||||
``.pth`` file).
|
||||
|
||||
3. All eggs installed by EasyInstall in any ``.pth`` file in each "site"
|
||||
directory (such as ``site-packages``), following the same ordering
|
||||
rules as for the ones on ``PYTHONPATH``.
|
||||
|
||||
4. The ``PYTHONPATH`` directories themselves, in their original order
|
||||
|
||||
5. Any paths from ``.pth`` files found on ``PYTHONPATH`` that were *not*
|
||||
eggs installed by EasyInstall, again following the same relative
|
||||
ordering rules.
|
||||
|
||||
6. The standard library and "site" directories, along with the contents
|
||||
of any ``.pth`` files found in the "site" directories.
|
||||
|
||||
Notice that sections 1, 4, and 6 comprise the "normal" Python setup for
|
||||
``sys.path``. Sections 2 and 3 are inserted to support eggs, and
|
||||
section 5 emulates what the "normal" semantics of ``.pth`` files on
|
||||
``PYTHONPATH`` would be if Python natively supported them.
|
||||
|
||||
For further discussion of the tradeoffs that went into this design, as
|
||||
well as notes on the actual magic inserted into ``.pth`` files to make
|
||||
them do these things, please see also the following messages to the
|
||||
distutils-SIG mailing list:
|
||||
|
||||
* http://mail.python.org/pipermail/distutils-sig/2006-February/006026.html
|
||||
* http://mail.python.org/pipermail/distutils-sig/2006-March/006123.html
|
||||
|
||||
|
||||
Script Wrappers
|
||||
---------------
|
||||
|
||||
EasyInstall never directly installs a project's original scripts to
|
||||
a script installation directory. Instead, it writes short wrapper
|
||||
scripts that first ensure that the project's dependencies are active
|
||||
on sys.path, before invoking the original script. These wrappers
|
||||
have a #! line that points to the version of Python that was used to
|
||||
install them, and their second line is always a comment that indicates
|
||||
the type of script wrapper, the project version required for the script
|
||||
to run, and information identifying the script to be invoked.
|
||||
|
||||
The format of this marker line is::
|
||||
|
||||
"# EASY-INSTALL-" script_type ": " tuple_of_strings "\n"
|
||||
|
||||
The ``script_type`` is one of ``SCRIPT``, ``DEV-SCRIPT``, or
|
||||
``ENTRY-SCRIPT``. The ``tuple_of_strings`` is a comma-separated
|
||||
sequence of Python string constants. For ``SCRIPT`` and ``DEV-SCRIPT``
|
||||
wrappers, there are two strings: the project version requirement, and
|
||||
the script name (as a filename within the ``scripts`` metadata
|
||||
directory). For ``ENTRY-SCRIPT`` wrappers, there are three:
|
||||
the project version requirement, the entry point group name, and the
|
||||
entry point name. (See the "Automatic Script Creation" section in the
|
||||
setuptools manual for more information about entry point scripts.)
|
||||
|
||||
In each case, the project version requirement string will be a string
|
||||
parseable with the ``pkg_resources`` modules' ``Requirement.parse()``
|
||||
classmethod. The only difference between a ``SCRIPT`` wrapper and a
|
||||
``DEV-SCRIPT`` is that a ``DEV-SCRIPT`` actually executes the original
|
||||
source script in the project's source tree, and is created when the
|
||||
"setup.py develop" command is run. A ``SCRIPT`` wrapper, on the other
|
||||
hand, uses the "installed" script written to the ``EGG-INFO/scripts``
|
||||
subdirectory of the corresponding ``.egg`` zipfile or directory.
|
||||
(``.egg-info`` eggs do not have script wrappers associated with them,
|
||||
except in the "setup.py develop" case.)
|
||||
|
||||
The purpose of including the marker line in generated script wrappers is
|
||||
to facilitate introspection of installed scripts, and their relationship
|
||||
to installed eggs. For example, an uninstallation tool could use this
|
||||
data to identify what scripts can safely be removed, and/or identify
|
||||
what scripts would stop working if a particular egg is uninstalled.
|
||||
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
Welcome to Setuptools' documentation!
|
||||
=====================================
|
||||
|
||||
Setuptools is a fully-featured, actively-maintained, and stable library
|
||||
designed to facilitate packaging Python projects, where packaging includes:
|
||||
|
||||
- Python package and module definitions
|
||||
- Distribution package metadata
|
||||
- Test hooks
|
||||
- Project installation
|
||||
- Platform-specific details
|
||||
- Python 3 support
|
||||
|
||||
Documentation content:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
roadmap
|
||||
python3
|
||||
using
|
||||
setuptools
|
||||
easy_install
|
||||
pkg_resources
|
||||
development
|
||||
merge
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
Setuptools/Distribute Merge FAQ
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
How do I upgrade from Distribute?
|
||||
=================================
|
||||
|
||||
Distribute specifically prohibits installation of Setuptools 0.7 from Distribute 0.6. There are then two options for upgrading.
|
||||
|
||||
Note that after upgrading using either technique, the only option to downgrade to either version is to completely uninstall Distribute and Setuptools 0.7 versions before reinstalling an 0.6 release.
|
||||
|
||||
Use Distribute 0.7
|
||||
------------------
|
||||
|
||||
The PYPA has put together a compatibility wrapper, a new release of Distribute version 0.7. This package will install over Distribute 0.6.x installations and will replace Distribute with a simple wrapper that requires Setuptools 0.7 or later. This technique is experimental, but initial results indicate this technique is the easiest upgrade path.
|
||||
|
||||
|
||||
Uninstall
|
||||
---------
|
||||
|
||||
First, completely uninstall Distribute. Since Distribute does not have an automated installation routine, this process is manual. Follow the instructions in the README for uninstalling.
|
||||
|
||||
|
||||
How do I upgrade from Setuptools 0.6?
|
||||
=====================================
|
||||
|
||||
There are no special instructions for upgrading over older versions of Setuptools. Simply use `easy_install -U` or run the latest `ez_setup.py`.
|
||||
|
||||
Where does the merge occur?
|
||||
========================================================
|
||||
|
||||
The merge is occurring between the heads of the default branch of Distribute and the setuptools-0.6 branch of Setuptools. The Setuptools SVN repo has been converted to a Mercurial repo hosted on Bitbucket. The work is still underway, so the exact changesets included may change, although the anticipated merge targets are Setuptools at 0.6c12 and Distribute at 0.6.35.
|
||||
|
||||
What happens to other branches?
|
||||
========================================================
|
||||
|
||||
Distribute 0.7 was abandoned long ago and won't be included in the resulting code tree, but may be retained for posterity in the original repo.
|
||||
|
||||
Setuptools default branch (also 0.7 development) may also be abandoned or may be incorporated into the new merged line if desirable (and as resources allow).
|
||||
|
||||
What history is lost/changed?
|
||||
========================================================
|
||||
|
||||
As setuptools was not on Mercurial when the fork occurred and as Distribute did not include the full setuptools history (prior to the creation of the setuptools-0.6 branch), the two source trees were not compatible. In order to most effectively communicate the code history, the Distribute code was grafted onto the (originally private) setuptools Mercurial repo. Although this grafting maintained the full code history with names, dates, and changes, it did lose the original hashes of those changes. Therefore, references to changes by hash (including tags) are lost.
|
||||
|
||||
Additionally, any heads that were not actively merged into the Distribute 0.6.35 release were also omitted. As a result, the changesets included in the merge repo are those from the original setuptools repo and all changesets ancestral to the Distribute 0.6.35 release.
|
||||
|
||||
What features will be in the merged code base?
|
||||
========================================================
|
||||
|
||||
In general, all "features" added in distribute will be included in setuptools. Where there exist conflicts or undesirable features, we will be explicit about what these limitations are. Changes that are backward-incompatible from setuptools 0.6 to distribute will likely be removed, and these also will be well documented.
|
||||
|
||||
Bootstrapping scripts (ez_setup/distribute_setup) and docs, as with distribute, will be maintained in the repository and built as part of the release process. Documentation and bootstrapping scripts will be hosted at python.org, as they are with distribute now. Documentation at telecommunity will be updated to refer or redirect to the new, merged docs.
|
||||
|
||||
On the whole, the merged setuptools should be largely compatible with the latest releases of both setuptools and distribute and will be an easy transition for users of either library.
|
||||
|
||||
Who is invited to contribute? Who is excluded?
|
||||
========================================================
|
||||
|
||||
While we've worked privately to initiate this merge due to the potential sensitivity of the topic, no one is excluded from this effort. We invite all members of the community, especially those most familiar with Python packaging and its challenges to join us in the effort.
|
||||
|
||||
We have lots of ideas for how we'd like to improve the codebase, release process, everything. Like distribute, the post-merge setuptools will have its source hosted on bitbucket. (So if you're currently a distribute contributor, about the only thing that's going to change is the URL of the repository you follow.) Also like distribute, it'll support Python 3, and hopefully we'll soon merge Vinay Sajip's patches to make it run on Python 3 without needing 2to3 to be run on the code first.
|
||||
|
||||
While we've worked privately to initiate this merge due to the potential sensitivity of the topic, no one is excluded from this effort. We invite all members of the community, especially those most familiar with Python packaging and its challenges to join us in the effort.
|
||||
|
||||
Why Setuptools and not Distribute or another name?
|
||||
========================================================
|
||||
|
||||
We do, however, understand that this announcement might be unsettling for some. The setuptools name has been subjected to a lot of deprecation in recent years, so the idea that it will now be the preferred name instead of distribute might be somewhat difficult or disorienting for some. We considered use of another name (Distribute or an entirely new name), but that would serve to only complicate matters further. Instead, our goal is to simplify the packaging landscape but without losing any hard-won advancements. We hope that the people who worked to spread the first message will be equally enthusiastic about spreading the new one, and we especially look forward to seeing the new posters and slogans celebrating setuptools.
|
||||
|
||||
What is the timeframe of release?
|
||||
========================================================
|
||||
|
||||
There are no hard timeframes for any of this effort, although progress is underway and a draft merge is underway and being tested privately. As an unfunded volunteer effort, our time to put in on it is limited, and we've both had some recent health and other challenges that have made working on this difficult, which in part explains why we haven't met our original deadline of a completed merge before PyCon.
|
||||
|
||||
The final Setuptools 0.7 was cut on June 1, 2013 and will be released to PyPI shortly thereafter.
|
||||
|
||||
What version number can I expect for the new release?
|
||||
========================================================
|
||||
|
||||
The new release will roughly follow the previous trend for setuptools and release the new release as 0.7. This number is somewhat arbitrary, but we wanted something other than 0.6 to distinguish it from its ancestor forks but not 1.0 to avoid putting too much emphasis on the release itself and to focus on merging the functionality. In the future, the project will likely adopt a versioning scheme similar to semver to convey semantic meaning about the release in the version number.
|
||||
Vendored
-122
@@ -1,122 +0,0 @@
|
||||
Merge with Distribute
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In 2013, the fork of Distribute was merged back into Setuptools. This
|
||||
document describes some of the details of the merge.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
merge-faq
|
||||
|
||||
Process
|
||||
=======
|
||||
|
||||
In order to try to accurately reflect the fork and then re-merge of the
|
||||
projects, the merge process brought both code trees together into one
|
||||
repository and grafted the Distribute fork onto the Setuptools development
|
||||
line (as if it had been created as a branch in the first place).
|
||||
|
||||
The rebase to get distribute onto setuptools went something like this::
|
||||
|
||||
hg phase -d -f -r 26b4c29b62db
|
||||
hg rebase -s 26b4c29b62db -d 7a5cf59c78d7
|
||||
|
||||
The technique required a late version of mercurial (2.5) to work correctly.
|
||||
|
||||
The only code that was included was the code that was ancestral to the public
|
||||
releases of Distribute 0.6. Additionally, because Setuptools was not hosted
|
||||
on Mercurial at the time of the fork and because the Distribute fork did not
|
||||
include a complete conversion of the Setuptools history, the Distribute
|
||||
changesets had to be re-applied to a new, different conversion of the
|
||||
Setuptools SVN repository. As a result, all of the hashes have changed.
|
||||
|
||||
Distribute was grafted in a 'distribute' branch and the 'setuptools-0.6'
|
||||
branch was targeted for the merge. The 'setuptools' branch remains with
|
||||
unreleased code and may be incorporated in the future.
|
||||
|
||||
Reconciling Differences
|
||||
=======================
|
||||
|
||||
There were both technical and philosophical differences between Setuptools
|
||||
and Distribute. To reconcile these differences in a manageable way, the
|
||||
following technique was undertaken:
|
||||
|
||||
Create a 'Setuptools-Distribute merge' branch, based on a late release of
|
||||
Distribute (0.6.35). This was done with a00b441856c4.
|
||||
|
||||
In that branch, first remove code that is no longer relevant to
|
||||
Setuptools (such as the setuptools patching code).
|
||||
|
||||
Next, in the the merge branch, create another base from at the point where the
|
||||
fork occurred (such that the code is still essentially an older but pristine
|
||||
setuptools). This base can be found as 955792b069d0. This creates two heads
|
||||
in the merge branch, each with a basis in the fork.
|
||||
|
||||
Then, repeatedly copy changes for a
|
||||
single file or small group of files from a late revision of that file in the
|
||||
'setuptools-0.6' branch (1aae1efe5733 was used) and commit those changes on
|
||||
the setuptools-only head. That head is then merged with the head with
|
||||
Distribute changes. It is in this Mercurial
|
||||
merge operation that the fundamental differences between Distribute and
|
||||
Setuptools are reconciled, but since only a single file or small set of files
|
||||
are used, the scope is limited.
|
||||
|
||||
Finally, once all the challenging files have been reconciled and merged, the
|
||||
remaining changes from the setuptools-0.6 branch are merged, deferring to the
|
||||
reconciled changes (a1fa855a5a62 and 160ccaa46be0).
|
||||
|
||||
Originally, jaraco attempted all of this using anonymous heads in the
|
||||
Distribute branch, but later realized this technique made for a somewhat
|
||||
unclear merge process, so the changes were re-committed as described above
|
||||
for clarity. In this way, the "distribute" and "setuptools" branches can
|
||||
continue to track the official Distribute changesets.
|
||||
|
||||
Concessions
|
||||
===========
|
||||
|
||||
With the merge of Setuptools and Distribute, the following concessions were
|
||||
made:
|
||||
|
||||
Differences from setuptools 0.6c12:
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
* Python 3 support.
|
||||
* Improved support for GAE.
|
||||
* Support `PEP-370 <http://www.python.org/dev/peps/pep-0370/>`_ per-user site
|
||||
packages.
|
||||
* Sort order of Distributions in pkg_resources now prefers PyPI to external
|
||||
links (Distribute issue 163).
|
||||
* Python 2.4 or greater is required (drop support for Python 2.3).
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
* Wording of some output has changed to replace contractions with their
|
||||
canonical form (i.e. prefer "could not" to "couldn't").
|
||||
* Manifest files are only written for 32-bit .exe launchers.
|
||||
|
||||
Differences from Distribute 0.6.36:
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
* The _distribute property of the setuptools module has been removed.
|
||||
* Distributions are once again installed as zipped eggs by default, per the
|
||||
rationale given in `the seminal bug report
|
||||
<http://bugs.python.org/setuptools/issue33>`_ indicates that the feature
|
||||
should remain and no substantial justification was given in the `Distribute
|
||||
report <https://bitbucket.org/tarek/distribute/issue/19/>`_.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
* The patch for `#174 <https://bitbucket.org/tarek/distribute/issue/174>`_
|
||||
has been rolled-back, as the comment on the ticket indicates that the patch
|
||||
addressed a symptom and not the fundamental issue.
|
||||
* ``easy_install`` (the command) once again honors setup.cfg if found in the
|
||||
current directory. The "mis-behavior" characterized in `#99
|
||||
<https://bitbucket.org/tarek/distribute/issue/99>`_ is actually intended
|
||||
behavior, and no substantial rationale was given for the deviation.
|
||||
-1985
File diff suppressed because it is too large
Load Diff
-124
@@ -1,124 +0,0 @@
|
||||
=====================================================
|
||||
Supporting both Python 2 and Python 3 with Setuptools
|
||||
=====================================================
|
||||
|
||||
Starting with Distribute version 0.6.2 and Setuptools 0.7, the Setuptools
|
||||
project supported Python 3. Installing and
|
||||
using setuptools for Python 3 code works exactly the same as for Python 2
|
||||
code, but Setuptools 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.
|
||||
|
||||
|
||||
Setuptools as help during porting
|
||||
=================================
|
||||
|
||||
Setuptools 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.
|
||||
|
||||
Setuptools 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 Setuptools 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 older versions of setuptools
|
||||
=======================================================
|
||||
|
||||
Setuptools earlier than 0.7 does 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
|
||||
those versions of setuptools instead of Distribute under Python 2. This output
|
||||
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 Distribute or
|
||||
Setuptools 0.7 or later is required.
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
===============
|
||||
Release Process
|
||||
===============
|
||||
|
||||
In order to allow for rapid, predictable releases, Setuptools uses a
|
||||
mechanical technique for releases. The release script, ``release.py`` in the
|
||||
repository, defines the details of the releases, and is executed by the
|
||||
`jaraco.packaging <https://bitbucket.org/jaraco/jaraco.packaging>`_ release
|
||||
module. The script does some checks (some interactive) and fully automates
|
||||
the release process.
|
||||
|
||||
A Setuptools release manager must have maintainer access on PyPI to the
|
||||
project and administrative access to the BitBucket project.
|
||||
|
||||
Release Managers
|
||||
----------------
|
||||
|
||||
Currently, the project has one release manager, Jason R. Coombs.
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
=======
|
||||
Roadmap
|
||||
=======
|
||||
|
||||
Setuptools is primarily in maintenance mode. The project attempts to address
|
||||
user issues, concerns, and feature requests in a timely fashion.
|
||||
-2699
File diff suppressed because it is too large
Load Diff
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
================================
|
||||
Using Setuptools in your project
|
||||
================================
|
||||
|
||||
To use Setuptools in your project, the recommended way is to ship
|
||||
`ez_setup.py` alongside your `setup.py` script and call
|
||||
it at the very beginning of `setup.py` like this::
|
||||
|
||||
from ez_setup import use_setuptools
|
||||
use_setuptools()
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
"""Run the EasyInstall command"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
from setuptools.command.easy_install import main
|
||||
main()
|
||||
Vendored
-338
@@ -1,338 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Bootstrap setuptools installation
|
||||
|
||||
To use setuptools in your package's setup.py, include this
|
||||
file in the same directory and add this to the top of your setup.py::
|
||||
|
||||
from ez_setup import use_setuptools
|
||||
use_setuptools()
|
||||
|
||||
To require a specific version of setuptools, set a download
|
||||
mirror, or use an alternate download directory, simply supply
|
||||
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 tempfile
|
||||
import zipfile
|
||||
import optparse
|
||||
import subprocess
|
||||
import platform
|
||||
import textwrap
|
||||
import contextlib
|
||||
|
||||
from distutils import log
|
||||
|
||||
try:
|
||||
from site import USER_SITE
|
||||
except ImportError:
|
||||
USER_SITE = None
|
||||
|
||||
DEFAULT_VERSION = "3.6"
|
||||
DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
|
||||
|
||||
def _python_cmd(*args):
|
||||
"""
|
||||
Return True if the command succeeded.
|
||||
"""
|
||||
args = (sys.executable,) + args
|
||||
return subprocess.call(args) == 0
|
||||
|
||||
|
||||
def _install(archive_filename, install_args=()):
|
||||
with archive_context(archive_filename):
|
||||
# installing
|
||||
log.warn('Installing Setuptools')
|
||||
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
|
||||
|
||||
|
||||
def _build_egg(egg, archive_filename, to_dir):
|
||||
with archive_context(archive_filename):
|
||||
# building an egg
|
||||
log.warn('Building a Setuptools egg in %s', to_dir)
|
||||
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
|
||||
# returning the result
|
||||
log.warn(egg)
|
||||
if not os.path.exists(egg):
|
||||
raise IOError('Could not build the egg.')
|
||||
|
||||
|
||||
def get_zip_class():
|
||||
"""
|
||||
Supplement ZipFile class to support context manager for Python 2.6
|
||||
"""
|
||||
class ContextualZipFile(zipfile.ZipFile):
|
||||
def __enter__(self):
|
||||
return self
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.close
|
||||
return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \
|
||||
ContextualZipFile
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def archive_context(filename):
|
||||
# extracting the archive
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
log.warn('Extracting in %s', tmpdir)
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
with get_zip_class()(filename) as archive:
|
||||
archive.extractall()
|
||||
|
||||
# going in the directory
|
||||
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||
os.chdir(subdir)
|
||||
log.warn('Now working in %s', subdir)
|
||||
yield
|
||||
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
def _do_download(version, download_base, to_dir, download_delay):
|
||||
egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
|
||||
% (version, sys.version_info[0], sys.version_info[1]))
|
||||
if not os.path.exists(egg):
|
||||
archive = download_setuptools(version, download_base,
|
||||
to_dir, download_delay)
|
||||
_build_egg(egg, archive, to_dir)
|
||||
sys.path.insert(0, egg)
|
||||
|
||||
# Remove previously-imported pkg_resources if present (see
|
||||
# https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
|
||||
if 'pkg_resources' in sys.modules:
|
||||
del sys.modules['pkg_resources']
|
||||
|
||||
import setuptools
|
||||
setuptools.bootstrap_install_from = egg
|
||||
|
||||
|
||||
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=os.curdir, download_delay=15):
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
rep_modules = 'pkg_resources', 'setuptools'
|
||||
imported = set(sys.modules).intersection(rep_modules)
|
||||
try:
|
||||
import pkg_resources
|
||||
except ImportError:
|
||||
return _do_download(version, download_base, to_dir, download_delay)
|
||||
try:
|
||||
pkg_resources.require("setuptools>=" + version)
|
||||
return
|
||||
except pkg_resources.DistributionNotFound:
|
||||
return _do_download(version, download_base, to_dir, download_delay)
|
||||
except pkg_resources.VersionConflict as VC_err:
|
||||
if imported:
|
||||
msg = textwrap.dedent("""
|
||||
The required version of setuptools (>={version}) is not available,
|
||||
and can't be installed while this script is running. Please
|
||||
install a more recent version first, using
|
||||
'easy_install -U setuptools'.
|
||||
|
||||
(Currently using {VC_err.args[0]!r})
|
||||
""").format(VC_err=VC_err, version=version)
|
||||
sys.stderr.write(msg)
|
||||
sys.exit(2)
|
||||
|
||||
# otherwise, reload ok
|
||||
del pkg_resources, sys.modules['pkg_resources']
|
||||
return _do_download(version, download_base, to_dir, download_delay)
|
||||
|
||||
def _clean_check(cmd, target):
|
||||
"""
|
||||
Run the command to download target. If the command fails, clean up before
|
||||
re-raising the error.
|
||||
"""
|
||||
try:
|
||||
subprocess.check_call(cmd)
|
||||
except subprocess.CalledProcessError:
|
||||
if os.access(target, os.F_OK):
|
||||
os.unlink(target)
|
||||
raise
|
||||
|
||||
def download_file_powershell(url, target):
|
||||
"""
|
||||
Download the file at url to target using Powershell (which will validate
|
||||
trust). Raise an exception if the command cannot complete.
|
||||
"""
|
||||
target = os.path.abspath(target)
|
||||
ps_cmd = (
|
||||
"[System.Net.WebRequest]::DefaultWebProxy.Credentials = "
|
||||
"[System.Net.CredentialCache]::DefaultCredentials; "
|
||||
"(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)"
|
||||
% vars()
|
||||
)
|
||||
cmd = [
|
||||
'powershell',
|
||||
'-Command',
|
||||
ps_cmd,
|
||||
]
|
||||
_clean_check(cmd, target)
|
||||
|
||||
def has_powershell():
|
||||
if platform.system() != 'Windows':
|
||||
return False
|
||||
cmd = ['powershell', '-Command', 'echo test']
|
||||
devnull = open(os.path.devnull, 'wb')
|
||||
try:
|
||||
try:
|
||||
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||
except Exception:
|
||||
return False
|
||||
finally:
|
||||
devnull.close()
|
||||
return True
|
||||
|
||||
download_file_powershell.viable = has_powershell
|
||||
|
||||
def download_file_curl(url, target):
|
||||
cmd = ['curl', url, '--silent', '--output', target]
|
||||
_clean_check(cmd, target)
|
||||
|
||||
def has_curl():
|
||||
cmd = ['curl', '--version']
|
||||
devnull = open(os.path.devnull, 'wb')
|
||||
try:
|
||||
try:
|
||||
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||
except Exception:
|
||||
return False
|
||||
finally:
|
||||
devnull.close()
|
||||
return True
|
||||
|
||||
download_file_curl.viable = has_curl
|
||||
|
||||
def download_file_wget(url, target):
|
||||
cmd = ['wget', url, '--quiet', '--output-document', target]
|
||||
_clean_check(cmd, target)
|
||||
|
||||
def has_wget():
|
||||
cmd = ['wget', '--version']
|
||||
devnull = open(os.path.devnull, 'wb')
|
||||
try:
|
||||
try:
|
||||
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||
except Exception:
|
||||
return False
|
||||
finally:
|
||||
devnull.close()
|
||||
return True
|
||||
|
||||
download_file_wget.viable = has_wget
|
||||
|
||||
def download_file_insecure(url, target):
|
||||
"""
|
||||
Use Python to download the file, even though it cannot authenticate the
|
||||
connection.
|
||||
"""
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except ImportError:
|
||||
from urllib2 import urlopen
|
||||
src = dst = None
|
||||
try:
|
||||
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(target, "wb")
|
||||
dst.write(data)
|
||||
finally:
|
||||
if src:
|
||||
src.close()
|
||||
if dst:
|
||||
dst.close()
|
||||
|
||||
download_file_insecure.viable = lambda: True
|
||||
|
||||
def get_best_downloader():
|
||||
downloaders = [
|
||||
download_file_powershell,
|
||||
download_file_curl,
|
||||
download_file_wget,
|
||||
download_file_insecure,
|
||||
]
|
||||
|
||||
for dl in downloaders:
|
||||
if dl.viable():
|
||||
return dl
|
||||
|
||||
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader):
|
||||
"""
|
||||
Download setuptools from a specified location and return its filename
|
||||
|
||||
`version` should be a valid setuptools 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.
|
||||
|
||||
``downloader_factory`` should be a function taking no arguments and
|
||||
returning a function for downloading a URL to a target.
|
||||
"""
|
||||
# making sure we use the absolute path
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
zip_name = "setuptools-%s.zip" % version
|
||||
url = download_base + zip_name
|
||||
saveto = os.path.join(to_dir, zip_name)
|
||||
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||
log.warn("Downloading %s", url)
|
||||
downloader = downloader_factory()
|
||||
downloader(url, saveto)
|
||||
return os.path.realpath(saveto)
|
||||
|
||||
def _build_install_args(options):
|
||||
"""
|
||||
Build the arguments to 'python setup.py install' on the setuptools package
|
||||
"""
|
||||
return ['--user'] if options.user_install else []
|
||||
|
||||
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 setuptools package')
|
||||
parser.add_option(
|
||||
'--insecure', dest='downloader_factory', action='store_const',
|
||||
const=lambda: download_file_insecure, default=get_best_downloader,
|
||||
help='Use internal, non-validating downloader'
|
||||
)
|
||||
parser.add_option(
|
||||
'--version', help="Specify which version to download",
|
||||
default=DEFAULT_VERSION,
|
||||
)
|
||||
options, args = parser.parse_args()
|
||||
# positional arguments are ignored
|
||||
return options
|
||||
|
||||
def main():
|
||||
"""Install or upgrade setuptools and EasyInstall"""
|
||||
options = _parse_args()
|
||||
archive = download_setuptools(
|
||||
version=options.version,
|
||||
download_base=options.download_base,
|
||||
downloader_factory=options.downloader_factory,
|
||||
)
|
||||
return _install(archive, _build_install_args(options))
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Vendored
-335
@@ -1,335 +0,0 @@
|
||||
/* 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
|
||||
|
||||
To build for Windows RT, install both Visual Studio Express for Windows 8
|
||||
and for Windows Desktop (both freeware), create "win32" application using
|
||||
"Windows Desktop" version, create new "ARM" target via
|
||||
"Configuration Manager" menu and modify ".vcxproj" file by adding
|
||||
"<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>" tag
|
||||
as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM"
|
||||
properties.
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
-2843
File diff suppressed because it is too large
Load Diff
Vendored
-91
@@ -1,91 +0,0 @@
|
||||
"""
|
||||
Setuptools is released using 'jaraco.packaging.release'. To make a release,
|
||||
install jaraco.packaging and run 'python -m jaraco.packaging.release'
|
||||
"""
|
||||
|
||||
import re
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import pkg_resources
|
||||
|
||||
pkg_resources.require('jaraco.packaging>=2.0')
|
||||
pkg_resources.require('wheel')
|
||||
|
||||
|
||||
def before_upload():
|
||||
_linkify('CHANGES.txt', 'CHANGES (links).txt')
|
||||
BootstrapBookmark.add()
|
||||
|
||||
|
||||
def after_push():
|
||||
os.remove('CHANGES (links).txt')
|
||||
BootstrapBookmark.push()
|
||||
|
||||
files_with_versions = (
|
||||
'ez_setup.py', 'setuptools/version.py',
|
||||
)
|
||||
|
||||
# bdist_wheel must be included or pip will break
|
||||
dist_commands = 'sdist', 'bdist_wheel'
|
||||
|
||||
test_info = "Travis-CI tests: http://travis-ci.org/#!/jaraco/setuptools"
|
||||
|
||||
os.environ["SETUPTOOLS_INSTALL_WINDOWS_SPECIFIC_FILES"] = "1"
|
||||
|
||||
link_patterns = [
|
||||
r"(Issue )?#(?P<issue>\d+)",
|
||||
r"Pull Request ?#(?P<pull_request>\d+)",
|
||||
r"Distribute #(?P<distribute>\d+)",
|
||||
r"Buildout #(?P<buildout>\d+)",
|
||||
r"Old Setuptools #(?P<old_setuptools>\d+)",
|
||||
r"Jython #(?P<jython>\d+)",
|
||||
r"Python #(?P<python>\d+)",
|
||||
]
|
||||
|
||||
issue_urls = dict(
|
||||
pull_request='https://bitbucket.org'
|
||||
'/pypa/setuptools/pull-request/{pull_request}',
|
||||
issue='https://bitbucket.org/pypa/setuptools/issue/{issue}',
|
||||
distribute='https://bitbucket.org/tarek/distribute/issue/{distribute}',
|
||||
buildout='https://github.com/buildout/buildout/issues/{buildout}',
|
||||
old_setuptools='http://bugs.python.org/setuptools/issue{old_setuptools}',
|
||||
jython='http://bugs.jython.org/issue{jython}',
|
||||
python='http://bugs.python.org/issue{python}',
|
||||
)
|
||||
|
||||
|
||||
def _linkify(source, dest):
|
||||
pattern = '|'.join(link_patterns)
|
||||
with open(source) as source:
|
||||
out = re.sub(pattern, replacer, source.read())
|
||||
with open(dest, 'w') as dest:
|
||||
dest.write(out)
|
||||
|
||||
|
||||
def replacer(match):
|
||||
text = match.group(0)
|
||||
match_dict = match.groupdict()
|
||||
for key in match_dict:
|
||||
if match_dict[key]:
|
||||
url = issue_urls[key].format(**match_dict)
|
||||
return "`{text} <{url}>`_".format(text=text, url=url)
|
||||
|
||||
class BootstrapBookmark:
|
||||
name = 'bootstrap'
|
||||
|
||||
@classmethod
|
||||
def add(cls):
|
||||
cmd = ['hg', 'bookmark', '-i', cls.name, '-f']
|
||||
subprocess.Popen(cmd)
|
||||
|
||||
@classmethod
|
||||
def push(cls):
|
||||
"""
|
||||
Push the bootstrap bookmark
|
||||
"""
|
||||
push_command = ['hg', 'push', '-B', cls.name]
|
||||
# don't use check_call here because mercurial will return a non-zero
|
||||
# code even if it succeeds at pushing the bookmark (because there are
|
||||
# no changesets to be pushed). !dm mercurial
|
||||
subprocess.call(push_command)
|
||||
Vendored
-27
@@ -1,27 +0,0 @@
|
||||
[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 zip
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
||||
[pytest]
|
||||
addopts = --ignore tests/manual_test.py --ignore tests/shlib_test
|
||||
|
||||
Vendored
-217
@@ -1,217 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Distutils setup file, used to install or test 'setuptools'"""
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
import contextlib
|
||||
|
||||
# Allow to run setup.py from another directory.
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
src_root = None
|
||||
|
||||
from distutils.util import convert_path
|
||||
|
||||
command_ns = {}
|
||||
init_path = convert_path('setuptools/command/__init__.py')
|
||||
with open(init_path) as init_file:
|
||||
exec(init_file.read(), command_ns)
|
||||
|
||||
SETUP_COMMANDS = command_ns['__all__']
|
||||
|
||||
main_ns = {}
|
||||
ver_path = convert_path('setuptools/version.py')
|
||||
with open(ver_path) as ver_file:
|
||||
exec(ver_file.read(), main_ns)
|
||||
|
||||
import setuptools
|
||||
from setuptools.command.build_py import build_py as _build_py
|
||||
from setuptools.command.test import test as _test
|
||||
|
||||
scripts = []
|
||||
|
||||
def _gen_console_scripts():
|
||||
yield "easy_install = setuptools.command.easy_install:main"
|
||||
|
||||
# Gentoo distributions manage the python-version-specific scripts
|
||||
# themselves, so those platforms define an environment variable to
|
||||
# suppress the creation of the version-specific scripts.
|
||||
var_names = (
|
||||
'SETUPTOOLS_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT',
|
||||
'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT',
|
||||
)
|
||||
if any(os.environ.get(var) not in (None, "", "0") for var in var_names):
|
||||
return
|
||||
yield ("easy_install-{shortver} = setuptools.command.easy_install:main"
|
||||
.format(shortver=sys.version[:3]))
|
||||
|
||||
console_scripts = list(_gen_console_scripts())
|
||||
|
||||
|
||||
# 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"""
|
||||
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)
|
||||
|
||||
class test(_test):
|
||||
"""Specific test class to avoid rewriting the entry_points.txt"""
|
||||
def run(self):
|
||||
with self._save_entry_points():
|
||||
_test.run(self)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _save_entry_points(self):
|
||||
entry_points = os.path.join('setuptools.egg-info', 'entry_points.txt')
|
||||
|
||||
if not os.path.exists(entry_points):
|
||||
yield
|
||||
return
|
||||
|
||||
# save the content
|
||||
with open(entry_points, 'rb') as f:
|
||||
ep_content = f.read()
|
||||
|
||||
# run the tests
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
# restore the file
|
||||
with open(entry_points, 'wb') as f:
|
||||
f.write(ep_content)
|
||||
|
||||
|
||||
readme_file = io.open('README.txt', encoding='utf-8')
|
||||
|
||||
# the release script adds hyperlinks to issues
|
||||
if os.path.exists('CHANGES (links).txt'):
|
||||
changes_file = open('CHANGES (links).txt')
|
||||
else:
|
||||
# but if the release script has not run, fall back to the source file
|
||||
changes_file = open('CHANGES.txt')
|
||||
with readme_file:
|
||||
with changes_file:
|
||||
long_description = readme_file.read() + '\n' + changes_file.read()
|
||||
|
||||
package_data = {'setuptools': ['site-patch.py']}
|
||||
force_windows_specific_files = (
|
||||
os.environ.get("SETUPTOOLS_INSTALL_WINDOWS_SPECIFIC_FILES")
|
||||
not in (None, "", "0")
|
||||
)
|
||||
if sys.platform == 'win32' or force_windows_specific_files:
|
||||
package_data.setdefault('setuptools', []).extend(['*.exe'])
|
||||
package_data.setdefault('setuptools.command', []).extend(['*.xml'])
|
||||
|
||||
pytest_runner = ['pytest-runner'] if 'ptr' in sys.argv else []
|
||||
|
||||
setup_params = dict(
|
||||
name="setuptools",
|
||||
version=main_ns['__version__'],
|
||||
description="Easily download, build, install, upgrade, and uninstall "
|
||||
"Python packages",
|
||||
author="Python Packaging Authority",
|
||||
author_email="distutils-sig@python.org",
|
||||
license="PSF or ZPL",
|
||||
long_description = long_description,
|
||||
keywords = "CPAN PyPI distutils eggs package management",
|
||||
url = "https://pypi.python.org/pypi/setuptools",
|
||||
test_suite = 'setuptools.tests',
|
||||
src_root = src_root,
|
||||
packages = setuptools.find_packages(),
|
||||
package_data = package_data,
|
||||
|
||||
py_modules = ['pkg_resources', 'easy_install'],
|
||||
|
||||
zip_safe = True,
|
||||
|
||||
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",
|
||||
"test_runner = 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.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
|
||||
Programming Language :: Python :: 3.4
|
||||
Topic :: Software Development :: Libraries :: Python Modules
|
||||
Topic :: System :: Archiving :: Packaging
|
||||
Topic :: System :: Systems Administration
|
||||
Topic :: Utilities
|
||||
""").strip().splitlines(),
|
||||
extras_require = {
|
||||
"ssl:sys_platform=='win32'": "wincertstore==0.2",
|
||||
"certs": "certifi==1.0.1",
|
||||
},
|
||||
dependency_links = [
|
||||
'https://pypi.python.org/packages/source/c/certifi/certifi-1.0.1.tar.gz#md5=45f5cb94b8af9e1df0f9450a8f61b790',
|
||||
'https://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2',
|
||||
],
|
||||
scripts = [],
|
||||
tests_require = [
|
||||
'setuptools[ssl]',
|
||||
'pytest',
|
||||
],
|
||||
setup_requires = [
|
||||
] + pytest_runner,
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
dist = setuptools.setup(**setup_params)
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
"""Extensions to the 'distutils' for large or complex distributions"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import distutils.core
|
||||
import distutils.filelist
|
||||
from distutils.core import Command as _Command
|
||||
from distutils.util import convert_path
|
||||
from fnmatch import fnmatchcase
|
||||
|
||||
import setuptools.version
|
||||
from setuptools.extension import Extension
|
||||
from setuptools.dist import Distribution, Feature, _get_unpatched
|
||||
from setuptools.depends import Require
|
||||
from setuptools.compat import filterfalse
|
||||
|
||||
__all__ = [
|
||||
'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require',
|
||||
'find_packages'
|
||||
]
|
||||
|
||||
__version__ = setuptools.version.__version__
|
||||
|
||||
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']
|
||||
|
||||
|
||||
class PackageFinder(object):
|
||||
@classmethod
|
||||
def find(cls, where='.', exclude=(), include=('*',)):
|
||||
"""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).
|
||||
|
||||
'include' is a sequence of package names to include. If it's
|
||||
specified, only the named packages will be included. If it's not
|
||||
specified, all found packages will be included. 'include' can contain
|
||||
shell style wildcard patterns just like 'exclude'.
|
||||
|
||||
The list of included packages is built up first and then any
|
||||
explicitly excluded packages are removed from it.
|
||||
"""
|
||||
out = cls._find_packages_iter(convert_path(where))
|
||||
out = cls.require_parents(out)
|
||||
includes = cls._build_filter(*include)
|
||||
excludes = cls._build_filter('ez_setup', '*__pycache__', *exclude)
|
||||
out = filter(includes, out)
|
||||
out = filterfalse(excludes, out)
|
||||
return list(out)
|
||||
|
||||
@staticmethod
|
||||
def require_parents(packages):
|
||||
"""
|
||||
Exclude any apparent package that apparently doesn't include its
|
||||
parent.
|
||||
|
||||
For example, exclude 'foo.bar' if 'foo' is not present.
|
||||
"""
|
||||
found = []
|
||||
for pkg in packages:
|
||||
base, sep, child = pkg.rpartition('.')
|
||||
if base and base not in found:
|
||||
continue
|
||||
found.append(pkg)
|
||||
yield pkg
|
||||
|
||||
@staticmethod
|
||||
def _all_dirs(base_path):
|
||||
"""
|
||||
Return all dirs in base_path, relative to base_path
|
||||
"""
|
||||
for root, dirs, files in os.walk(base_path, followlinks=True):
|
||||
for dir in dirs:
|
||||
yield os.path.relpath(os.path.join(root, dir), base_path)
|
||||
|
||||
@classmethod
|
||||
def _find_packages_iter(cls, base_path):
|
||||
dirs = cls._all_dirs(base_path)
|
||||
suitable = filterfalse(lambda n: '.' in n, dirs)
|
||||
return (
|
||||
path.replace(os.path.sep, '.')
|
||||
for path in suitable
|
||||
if cls._looks_like_package(os.path.join(base_path, path))
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _looks_like_package(path):
|
||||
return os.path.isfile(os.path.join(path, '__init__.py'))
|
||||
|
||||
@staticmethod
|
||||
def _build_filter(*patterns):
|
||||
"""
|
||||
Given a list of patterns, return a callable that will be true only if
|
||||
the input matches one of the patterns.
|
||||
"""
|
||||
return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns)
|
||||
|
||||
class PEP420PackageFinder(PackageFinder):
|
||||
@staticmethod
|
||||
def _looks_like_package(path):
|
||||
return True
|
||||
|
||||
find_packages = PackageFinder.find
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
distutils.filelist.findall = findall # fix findall bug in distutils.
|
||||
|
||||
# sys.dont_write_bytecode was introduced in Python 2.6.
|
||||
_dont_write_bytecode = getattr(sys, 'dont_write_bytecode',
|
||||
bool(os.environ.get("PYTHONDONTWRITEBYTECODE")))
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
"""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, posixpath
|
||||
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.split('/'):
|
||||
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.split('/'):
|
||||
prelim_dst = os.path.join(extract_dir, *name.split('/'))
|
||||
|
||||
# resolve any links and to extract the link targets as normal files
|
||||
while member is not None and (member.islnk() or member.issym()):
|
||||
linkpath = member.linkname
|
||||
if member.issym():
|
||||
linkpath = posixpath.join(posixpath.dirname(member.name), linkpath)
|
||||
linkpath = posixpath.normpath(linkpath)
|
||||
member = tarobj._getmember(linkpath)
|
||||
|
||||
if member is not None and (member.isfile() or member.isdir()):
|
||||
final_dst = progress_filter(name, prelim_dst)
|
||||
if final_dst:
|
||||
if final_dst.endswith(os.sep):
|
||||
final_dst = final_dst[:-1]
|
||||
try:
|
||||
tarobj._extract_member(member, final_dst) # XXX Ugh
|
||||
except tarfile.ExtractError:
|
||||
pass # chown/chmod/mkfifo/mknode/makedev failed
|
||||
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.
BIN
Binary file not shown.
@@ -1,17 +0,0 @@
|
||||
__all__ = [
|
||||
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
|
||||
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
|
||||
'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts',
|
||||
'register', 'bdist_wininst', 'upload_docs',
|
||||
]
|
||||
|
||||
from setuptools.command import install_scripts
|
||||
import sys
|
||||
|
||||
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
|
||||
@@ -1,76 +0,0 @@
|
||||
from distutils.errors import DistutilsOptionError
|
||||
|
||||
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
|
||||
@@ -1,462 +0,0 @@
|
||||
"""setuptools.command.bdist_egg
|
||||
|
||||
Build .egg distributions"""
|
||||
|
||||
# This module should be kept compatible with Python 2.3
|
||||
import sys
|
||||
import os
|
||||
import marshal
|
||||
import textwrap
|
||||
from setuptools import Command
|
||||
from distutils.dir_util import remove_tree, mkpath
|
||||
try:
|
||||
# Python 2.7 or >=3.2
|
||||
from sysconfig import get_path, get_python_version
|
||||
def _get_purelib():
|
||||
return get_path("purelib")
|
||||
except ImportError:
|
||||
from distutils.sysconfig import get_python_lib, get_python_version
|
||||
def _get_purelib():
|
||||
return get_python_lib(False)
|
||||
|
||||
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.compat import basestring, next
|
||||
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):
|
||||
_stub_template = textwrap.dedent("""
|
||||
def __bootstrap__():
|
||||
global __bootstrap__, __loader__, __file__
|
||||
import sys, pkg_resources, imp
|
||||
__file__ = pkg_resources.resource_filename(__name__, %r)
|
||||
__loader__ = None; del __bootstrap__, __loader__
|
||||
imp.load_dynamic(__name__,__file__)
|
||||
__bootstrap__()
|
||||
""").lstrip()
|
||||
with open(pyfile, 'w') as f:
|
||||
f.write(_stub_template % resource)
|
||||
|
||||
|
||||
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_purelib()))
|
||||
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
|
||||
if self.distribution.has_c_libraries() and not self.skip_build:
|
||||
self.run_command('build_clib')
|
||||
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,'/')
|
||||
|
||||
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 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 = next(walker)
|
||||
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
|
||||
@@ -1,42 +0,0 @@
|
||||
import distutils.command.bdist_rpm as orig
|
||||
|
||||
class bdist_rpm(orig.bdist_rpm):
|
||||
"""
|
||||
Override the default bdist_rpm behavior to do the following:
|
||||
|
||||
1. Run egg_info to ensure the name and version are properly calculated.
|
||||
2. Always run 'install' using --single-version-externally-managed to
|
||||
disable eggs in RPM distributions.
|
||||
3. Replace dash with underscore in the version numbers for better RPM
|
||||
compatibility.
|
||||
"""
|
||||
|
||||
def run(self):
|
||||
# ensure distro name is up-to-date
|
||||
self.run_command('egg_info')
|
||||
|
||||
orig.bdist_rpm.run(self)
|
||||
|
||||
def _make_spec_file(self):
|
||||
version = self.distribution.get_version()
|
||||
rpmversion = version.replace('-','_')
|
||||
spec = orig.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
|
||||
]
|
||||
insert_loc = spec.index(line24) + 1
|
||||
unmangled_version = "%define unmangled_version " + version
|
||||
spec.insert(insert_loc, unmangled_version)
|
||||
return spec
|
||||
@@ -1,20 +0,0 @@
|
||||
import distutils.command.bdist_wininst as orig
|
||||
|
||||
class bdist_wininst(orig.bdist_wininst):
|
||||
def reinitialize_command(self, command, reinit_subcommands=0):
|
||||
"""
|
||||
Supplement reinitialize_command to work around
|
||||
http://bugs.python.org/issue20819
|
||||
"""
|
||||
cmd = self.distribution.reinitialize_command(
|
||||
command, reinit_subcommands)
|
||||
if command in ('install', 'install_lib'):
|
||||
cmd.install_lib = None
|
||||
return cmd
|
||||
|
||||
def run(self):
|
||||
self._is_running = True
|
||||
try:
|
||||
orig.bdist_wininst.run(self)
|
||||
finally:
|
||||
self._is_running = False
|
||||
@@ -1,289 +0,0 @@
|
||||
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
|
||||
import 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
|
||||
try:
|
||||
# Python 2.7 or >=3.2
|
||||
from sysconfig import _CONFIG_VARS
|
||||
except ImportError:
|
||||
from distutils.sysconfig import get_config_var
|
||||
get_config_var("LDSHARED") # make sure _config_vars is initialized
|
||||
del get_config_var
|
||||
from distutils.sysconfig import _config_vars as _CONFIG_VARS
|
||||
from distutils import log
|
||||
from distutils.errors import DistutilsError
|
||||
|
||||
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 in self.ext_map:
|
||||
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)
|
||||
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
|
||||
)
|
||||
@@ -1,221 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import fnmatch
|
||||
import textwrap
|
||||
import distutils.command.build_py as orig
|
||||
from distutils.util import convert_path
|
||||
from glob import glob
|
||||
|
||||
try:
|
||||
from setuptools.lib2to3_ex import Mixin2to3
|
||||
except ImportError:
|
||||
class Mixin2to3:
|
||||
def run_2to3(self, files, doctests=True):
|
||||
"do nothing"
|
||||
|
||||
class build_py(orig.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):
|
||||
orig.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(orig.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 orig.build_py.__getattr__(self,attr)
|
||||
|
||||
def build_module(self, module, module_file, package):
|
||||
outfile, copied = orig.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"""
|
||||
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 orig.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 = orig.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.errors import DistutilsError
|
||||
raise DistutilsError(
|
||||
"Namespace package problem: %s is a namespace package, but its\n"
|
||||
"__init__.py does not call declare_namespace()! Please fix it.\n"
|
||||
'(See the setuptools manual under "Namespace Packages" for '
|
||||
"details.)\n" % (package,)
|
||||
)
|
||||
f.close()
|
||||
return init_py
|
||||
|
||||
def initialize_options(self):
|
||||
self.packages_checked={}
|
||||
orig.build_py.initialize_options(self)
|
||||
|
||||
def get_package_dir(self, package):
|
||||
res = orig.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
|
||||
msg = textwrap.dedent("""
|
||||
Error: setup script specifies an absolute path:
|
||||
|
||||
%s
|
||||
|
||||
setup() arguments must *always* be /-separated paths relative to the
|
||||
setup.py directory, *never* absolute paths.
|
||||
""").lstrip() % path
|
||||
raise DistutilsSetupError(msg)
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
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
@@ -1,392 +0,0 @@
|
||||
"""setuptools.command.egg_info
|
||||
|
||||
Create a distribution's .egg-info directory and contents"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from setuptools import Command
|
||||
import distutils.errors
|
||||
from distutils import log
|
||||
from setuptools.command.sdist import sdist
|
||||
from setuptools.compat import basestring
|
||||
from setuptools import svn_utils
|
||||
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 setuptools.command.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 setuptools.command.setopt import edit_config
|
||||
values = dict(
|
||||
egg_info=dict(
|
||||
tag_svn_revision=0,
|
||||
tag_date=0,
|
||||
tag_build=self.tags(),
|
||||
)
|
||||
)
|
||||
edit_config(filename, values)
|
||||
|
||||
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 distutils.errors.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
|
||||
|
||||
@staticmethod
|
||||
def get_svn_revision():
|
||||
return str(svn_utils.SvnInfo.load(os.curdir).get_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)
|
||||
from setuptools.command 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 sorted(ep.items()):
|
||||
if not isinstance(contents,basestring):
|
||||
contents = EntryPoint.parse_group(section, contents)
|
||||
contents = '\n'.join(sorted(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
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
import setuptools
|
||||
import inspect
|
||||
import glob
|
||||
import warnings
|
||||
import platform
|
||||
import distutils.command.install as orig
|
||||
from distutils.errors import DistutilsArgError
|
||||
|
||||
# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for
|
||||
# now. See https://bitbucket.org/pypa/setuptools/issue/199/
|
||||
_install = orig.install
|
||||
|
||||
class install(orig.install):
|
||||
"""Use easy_install to install the package, w/dependencies"""
|
||||
|
||||
user_options = orig.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 = orig.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):
|
||||
orig.install.initialize_options(self)
|
||||
self.old_and_unmanageable = None
|
||||
self.single_version_externally_managed = None
|
||||
|
||||
def finalize_options(self):
|
||||
orig.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 orig.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 orig.install.run(self)
|
||||
|
||||
if not self._called_from_setup(inspect.currentframe()):
|
||||
# Run in backward-compatibility mode to support bdist_* commands.
|
||||
orig.install.run(self)
|
||||
else:
|
||||
self.do_egg_install()
|
||||
|
||||
@staticmethod
|
||||
def _called_from_setup(run_frame):
|
||||
"""
|
||||
Attempt to detect whether run() was called from setup() or by another
|
||||
command. If called by setup(), the parent caller will be the
|
||||
'run_command' method in 'distutils.dist', and *its* caller will be
|
||||
the 'run_commands' method. If called any other way, the
|
||||
immediate caller *might* be 'run_command', but it won't have been
|
||||
called by 'run_commands'. Return True in that case or if a call stack
|
||||
is unavailable. Return False otherwise.
|
||||
"""
|
||||
if run_frame is None:
|
||||
msg = "Call stack not available. bdist_* commands may fail."
|
||||
warnings.warn(msg)
|
||||
if platform.python_implementation() == 'IronPython':
|
||||
msg = "For best results, pass -X:Frames to enable call stack."
|
||||
warnings.warn(msg)
|
||||
return True
|
||||
res = inspect.getouterframes(run_frame)[2]
|
||||
caller, = res[:1]
|
||||
info = inspect.getframeinfo(caller)
|
||||
caller_module = caller.f_globals.get('__name__', '')
|
||||
return (
|
||||
caller_module == 'distutils.dist'
|
||||
and info.function == 'run_commands'
|
||||
)
|
||||
|
||||
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 orig.install.sub_commands if cmd[0] not in install._nc
|
||||
] + install.new_commands
|
||||
@@ -1,125 +0,0 @@
|
||||
from setuptools import Command
|
||||
from setuptools.archive_util import unpack_archive
|
||||
from distutils import log, dir_util
|
||||
import os, 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
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import distutils.command.install_lib as orig
|
||||
import os
|
||||
|
||||
class install_lib(orig.install_lib):
|
||||
"""Don't add compiled flags to filenames of non-Python 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
|
||||
svem = (nsp and self.get_finalized_command('install')
|
||||
.single_version_externally_managed)
|
||||
if svem:
|
||||
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 orig.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 = orig.install_lib.get_outputs(self)
|
||||
exclude = self.get_exclusions()
|
||||
if exclude:
|
||||
return [f for f in outputs if f not in exclude]
|
||||
return outputs
|
||||
@@ -1,52 +0,0 @@
|
||||
import distutils.command.install_scripts as orig
|
||||
from pkg_resources import Distribution, PathMetadata, ensure_directory
|
||||
import os
|
||||
from distutils import log
|
||||
|
||||
class install_scripts(orig.install_scripts):
|
||||
"""Do normal script install, plus any egg_info wrapper scripts"""
|
||||
|
||||
def initialize_options(self):
|
||||
orig.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:
|
||||
orig.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, 0o777-mask)
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="1.0.0.0"
|
||||
processorArchitecture="X86"
|
||||
name="%(name)s"
|
||||
type="win32"/>
|
||||
<!-- Identify the application security requirements. -->
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
@@ -1,9 +0,0 @@
|
||||
import distutils.command.register as orig
|
||||
|
||||
class register(orig.register):
|
||||
__doc__ = orig.register.__doc__
|
||||
|
||||
def run(self):
|
||||
# Make sure that we are using valid current name/version info
|
||||
self.run_command('egg_info')
|
||||
orig.register.run(self)
|
||||
@@ -1,58 +0,0 @@
|
||||
import os
|
||||
from setuptools import Command
|
||||
from setuptools.compat import basestring
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
from distutils.errors import DistutilsOptionError
|
||||
|
||||
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)
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
settings = {}
|
||||
|
||||
for cmd in dist.command_options:
|
||||
|
||||
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)
|
||||
|
||||
-244
@@ -1,244 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from glob import glob
|
||||
|
||||
import pkg_resources
|
||||
import distutils.command.sdist as orig
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
from setuptools import svn_utils
|
||||
|
||||
READMES = ('README', 'README.rst', 'README.txt')
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
#TODO will need test case
|
||||
class re_finder(object):
|
||||
"""
|
||||
Finder that locates files based on entries in a file matched by a
|
||||
regular expression.
|
||||
"""
|
||||
|
||||
def __init__(self, path, pattern, postproc=lambda x: x):
|
||||
self.pattern = pattern
|
||||
self.postproc = postproc
|
||||
self.entries_path = convert_path(path)
|
||||
|
||||
def _finder(self, dirname, filename):
|
||||
f = open(filename,'rU')
|
||||
try:
|
||||
data = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
for match in self.pattern.finditer(data):
|
||||
path = match.group(1)
|
||||
# postproc was formerly used when the svn finder
|
||||
# was an re_finder for calling unescape
|
||||
path = self.postproc(path)
|
||||
yield svn_utils.joinpath(dirname, path)
|
||||
|
||||
def find(self, dirname=''):
|
||||
path = svn_utils.joinpath(dirname, self.entries_path)
|
||||
|
||||
if not os.path.isfile(path):
|
||||
# entries file doesn't exist
|
||||
return
|
||||
for path in self._finder(dirname,path):
|
||||
if os.path.isfile(path):
|
||||
yield path
|
||||
elif os.path.isdir(path):
|
||||
for item in self.find(path):
|
||||
yield item
|
||||
__call__ = find
|
||||
|
||||
|
||||
def _default_revctrl(dirname=''):
|
||||
'Primary svn_cvs entry point'
|
||||
for finder in finders:
|
||||
for item in finder(dirname):
|
||||
yield item
|
||||
|
||||
|
||||
finders = [
|
||||
re_finder('CVS/Entries', re.compile(r"^\w?/([^/]+)/", re.M)),
|
||||
svn_utils.svn_finder,
|
||||
]
|
||||
|
||||
|
||||
class sdist(orig.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 __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:
|
||||
orig.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.
|
||||
has_leaky_handle = (
|
||||
sys.version_info < (2,7,2)
|
||||
or (3,0) <= sys.version_info < (3,1,4)
|
||||
or (3,2) <= sys.version_info < (3,2,1)
|
||||
)
|
||||
if has_leaky_handle:
|
||||
read_template = __read_template_hack
|
||||
|
||||
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 = list(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 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):
|
||||
orig.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()
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
import os
|
||||
import distutils
|
||||
from setuptools import Command
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
from distutils.errors import DistutilsOptionError
|
||||
|
||||
__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 setuptools.compat import ConfigParser
|
||||
log.debug("Reading configuration from %s", filename)
|
||||
opts = ConfigParser.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:
|
||||
with open(filename, 'w') as f:
|
||||
opts.write(f)
|
||||
|
||||
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
|
||||
)
|
||||
-179
@@ -1,179 +0,0 @@
|
||||
import unittest
|
||||
from unittest import TestLoader
|
||||
|
||||
from setuptools import Command
|
||||
from distutils.errors import DistutilsOptionError
|
||||
import sys
|
||||
from pkg_resources import (resource_listdir, resource_exists,
|
||||
normalize_path, working_set, _namespace_packages, add_activation_listener,
|
||||
require, EntryPoint)
|
||||
|
||||
from setuptools.py31compat import unittest_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')"),
|
||||
('test-runner=', 'r', "Test runner to use"),
|
||||
]
|
||||
|
||||
def initialize_options(self):
|
||||
self.test_suite = None
|
||||
self.test_module = None
|
||||
self.test_loader = None
|
||||
self.test_runner = 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"
|
||||
if self.test_runner is None:
|
||||
self.test_runner = getattr(self.distribution, 'test_runner', None)
|
||||
|
||||
def with_project_on_sys_path(self, func):
|
||||
with_2to3 = (
|
||||
sys.version_info >= (3,)
|
||||
and getattr(self.distribution, 'use_2to3', False)
|
||||
)
|
||||
if with_2to3:
|
||||
# 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):
|
||||
# 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)
|
||||
list(map(sys.modules.__delitem__, del_modules))
|
||||
|
||||
unittest_main(
|
||||
None, None, [unittest.__file__]+self.test_args,
|
||||
testLoader=self._resolve_as_ep(self.test_loader),
|
||||
testRunner=self._resolve_as_ep(self.test_runner),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_as_ep(val):
|
||||
"""
|
||||
Load the indicated attribute value, called, as a as if it were
|
||||
specified as an entry point.
|
||||
"""
|
||||
if val is None:
|
||||
return
|
||||
parsed = EntryPoint.parse("x=" + val)
|
||||
return parsed.load(require=False)()
|
||||
@@ -1,193 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""upload_docs
|
||||
|
||||
Implements a Distutils 'upload_docs' subcommand (upload documentation to
|
||||
PyPI's pythonhosted.org).
|
||||
"""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import zipfile
|
||||
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
|
||||
from distutils.command.upload import upload
|
||||
|
||||
from setuptools.compat import httplib, urlparse, unicode, iteritems, PY3
|
||||
|
||||
errors = 'surrogateescape' if PY3 else 'strict'
|
||||
|
||||
|
||||
# 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, errors)
|
||||
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 PY3:
|
||||
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 iteritems(data):
|
||||
title = '\nContent-Disposition: form-data; name="%s"' % key
|
||||
# handle multiple entries for the same name
|
||||
if not isinstance(values, list):
|
||||
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(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 = ''
|
||||
try:
|
||||
conn.connect()
|
||||
conn.putrequest("POST", url)
|
||||
content_type = 'multipart/form-data; boundary=%s' % boundary
|
||||
conn.putheader('Content-type', content_type)
|
||||
conn.putheader('Content-length', str(len(body)))
|
||||
conn.putheader('Authorization', auth)
|
||||
conn.endheaders()
|
||||
conn.send(body)
|
||||
except socket.error:
|
||||
e = sys.exc_info()[1]
|
||||
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 = 'https://pythonhosted.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)
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
import sys
|
||||
import itertools
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
PY3 = False
|
||||
|
||||
basestring = basestring
|
||||
import __builtin__ as builtins
|
||||
import ConfigParser
|
||||
from StringIO import StringIO
|
||||
BytesIO = StringIO
|
||||
execfile = execfile
|
||||
func_code = lambda o: o.func_code
|
||||
func_globals = lambda o: o.func_globals
|
||||
im_func = lambda o: o.im_func
|
||||
from htmlentitydefs import name2codepoint
|
||||
import httplib
|
||||
from BaseHTTPServer import HTTPServer
|
||||
from SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler
|
||||
iteritems = lambda o: o.iteritems()
|
||||
long_type = long
|
||||
maxsize = sys.maxint
|
||||
next = lambda o: o.next()
|
||||
numeric_types = (int, long, float)
|
||||
unichr = unichr
|
||||
unicode = unicode
|
||||
bytes = str
|
||||
from urllib import url2pathname, splittag, pathname2url
|
||||
import urllib2
|
||||
from urllib2 import urlopen, HTTPError, URLError, unquote, splituser
|
||||
from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit
|
||||
filterfalse = itertools.ifilterfalse
|
||||
|
||||
exec("""def reraise(tp, value, tb=None):
|
||||
raise tp, value, tb""")
|
||||
else:
|
||||
PY3 = True
|
||||
|
||||
basestring = str
|
||||
import builtins
|
||||
import configparser as ConfigParser
|
||||
from io import StringIO, BytesIO
|
||||
func_code = lambda o: o.__code__
|
||||
func_globals = lambda o: o.__globals__
|
||||
im_func = lambda o: o.__func__
|
||||
from html.entities import name2codepoint
|
||||
import http.client as httplib
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
iteritems = lambda o: o.items()
|
||||
long_type = int
|
||||
maxsize = sys.maxsize
|
||||
next = next
|
||||
numeric_types = (int, float)
|
||||
unichr = chr
|
||||
unicode = str
|
||||
bytes = bytes
|
||||
from urllib.error import HTTPError, URLError
|
||||
import urllib.request as urllib2
|
||||
from urllib.request import urlopen, url2pathname, pathname2url
|
||||
from urllib.parse import (
|
||||
urlparse, urlunparse, unquote, splituser, urljoin, urlsplit,
|
||||
urlunsplit, splittag,
|
||||
)
|
||||
filterfalse = itertools.filterfalse
|
||||
|
||||
def execfile(fn, globs=None, locs=None):
|
||||
if globs is None:
|
||||
globs = globals()
|
||||
if locs is None:
|
||||
locs = globs
|
||||
f = open(fn, 'rb')
|
||||
try:
|
||||
source = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
exec(compile(source, fn, 'exec'), globs, locs)
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
-246
@@ -1,246 +0,0 @@
|
||||
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 * long_type(65536)
|
||||
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')
|
||||
|
||||
|
||||
-818
@@ -1,818 +0,0 @@
|
||||
__all__ = ['Distribution']
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
import distutils.log
|
||||
import distutils.core
|
||||
import distutils.cmd
|
||||
import distutils.dist
|
||||
from distutils.core import Distribution as _Distribution
|
||||
from distutils.errors import (DistutilsOptionError, DistutilsPlatformError,
|
||||
DistutilsSetupError)
|
||||
|
||||
from setuptools.depends import Require
|
||||
from setuptools.compat import numeric_types, basestring
|
||||
import pkg_resources
|
||||
|
||||
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)
|
||||
|
||||
def _patch_distribution_metadata_write_pkg_info():
|
||||
"""
|
||||
Workaround issue #197 - Python 3.1 uses an environment-local encoding to
|
||||
save the pkg_info. Monkey-patch its write_pkg_info method to correct
|
||||
this undesirable behavior.
|
||||
"""
|
||||
if sys.version_info[:2] != (3,1):
|
||||
return
|
||||
|
||||
# from Python 3.4
|
||||
def write_pkg_info(self, base_dir):
|
||||
"""Write the PKG-INFO file into the release tree.
|
||||
"""
|
||||
with open(os.path.join(base_dir, 'PKG-INFO'), 'w',
|
||||
encoding='UTF-8') as pkg_info:
|
||||
self.write_pkg_file(pkg_info)
|
||||
|
||||
distutils.dist.DistributionMetadata.write_pkg_info = write_pkg_info
|
||||
_patch_distribution_metadata_write_pkg_info()
|
||||
|
||||
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(
|
||||
"WARNING: %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():
|
||||
if ':' in k:
|
||||
k,m = k.split(':',1)
|
||||
if pkg_resources.invalid_marker(m):
|
||||
raise DistutilsSetupError("Invalid environment marker: "+m)
|
||||
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 = sys.exc_info()[1]
|
||||
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"
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
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' **deprecated** -- 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 = {}
|
||||
_attrs_dict = attrs or {}
|
||||
if 'features' in _attrs_dict or 'require_features' in _attrs_dict:
|
||||
Feature.warn_deprecated()
|
||||
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, numeric_types):
|
||||
# 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,
|
||||
replace_conflicting=True
|
||||
):
|
||||
working_set.add(dist, replace=True)
|
||||
|
||||
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 list(opts):
|
||||
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,)
|
||||
)
|
||||
list(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:
|
||||
"""
|
||||
**deprecated** -- The `Feature` facility was never completely implemented
|
||||
or supported, `has reported issues
|
||||
<https://bitbucket.org/pypa/setuptools/issue/58>`_ and will be removed in
|
||||
a future version.
|
||||
|
||||
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'.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def warn_deprecated():
|
||||
warnings.warn(
|
||||
"Features are deprecated and will be removed in a future "
|
||||
"version. See http://bitbucket.org/pypa/setuptools/65.",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
||||
def __init__(self, description, standard=False, available=True,
|
||||
optional=True, require_features=(), remove=(), **extras):
|
||||
self.warn_deprecated()
|
||||
|
||||
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)
|
||||
)
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
import sys
|
||||
import re
|
||||
import functools
|
||||
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)
|
||||
self._convert_pyx_sources_to_lang()
|
||||
|
||||
def _convert_pyx_sources_to_lang(self):
|
||||
"""
|
||||
Replace sources with .pyx extensions to sources with the target
|
||||
language extension. This mechanism allows language authors to supply
|
||||
pre-converted sources but to prefer the .pyx sources.
|
||||
"""
|
||||
if have_pyrex():
|
||||
# the build has Cython, so allow it to compile the .pyx files
|
||||
return
|
||||
lang = self.language or ''
|
||||
target_ext = '.cpp' if lang.lower() == 'c++' else '.c'
|
||||
sub = functools.partial(re.sub, '.pyx$', target_ext)
|
||||
self.sources = list(map(sub, 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.
BIN
Binary file not shown.
-58
@@ -1,58 +0,0 @@
|
||||
"""
|
||||
Customized Mixin2to3 support:
|
||||
|
||||
- adds support for converting doctests
|
||||
|
||||
|
||||
This module raises an ImportError on Python 2.
|
||||
"""
|
||||
|
||||
from distutils.util import Mixin2to3 as _Mixin2to3
|
||||
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)
|
||||
-1058
File diff suppressed because it is too large
Load Diff
-19
@@ -1,19 +0,0 @@
|
||||
"""
|
||||
Compatibility Support for Python 2.6 and earlier
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from setuptools.compat import splittag
|
||||
|
||||
def strip_fragment(url):
|
||||
"""
|
||||
In `Python 8280 <http://bugs.python.org/issue8280>`_, Python 2.7 and
|
||||
later was patched to disregard the fragment when making URL requests.
|
||||
Do the same for Python 2.6 and earlier.
|
||||
"""
|
||||
url, fragment = splittag(url)
|
||||
return url
|
||||
|
||||
if sys.version_info >= (2,7):
|
||||
strip_fragment = lambda x: x
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
"""
|
||||
Compatibility Support for Python 2.7 and earlier
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
def get_all_headers(message, key):
|
||||
"""
|
||||
Given an HTTPMessage, return all headers matching a given key.
|
||||
"""
|
||||
return message.get_all(key)
|
||||
|
||||
if sys.version_info < (3,):
|
||||
def get_all_headers(message, key):
|
||||
return message.getheaders(key)
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
__all__ = ['get_config_vars', 'get_path']
|
||||
|
||||
try:
|
||||
# Python 2.7 or >=3.2
|
||||
from sysconfig import get_config_vars, get_path
|
||||
except ImportError:
|
||||
from distutils.sysconfig import get_config_vars, get_python_lib
|
||||
def get_path(name):
|
||||
if name not in ('platlib', 'purelib'):
|
||||
raise ValueError("Name must be purelib or platlib")
|
||||
return get_python_lib(name=='platlib')
|
||||
|
||||
try:
|
||||
# Python >=3.2
|
||||
from tempfile import TemporaryDirectory
|
||||
except ImportError:
|
||||
import shutil
|
||||
import tempfile
|
||||
class TemporaryDirectory(object):
|
||||
""""
|
||||
Very simple temporary directory context manager.
|
||||
Will try to delete afterward, but will also ignore OS and similar
|
||||
errors on deletion.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.name = None # Handle mkdtemp raising an exception
|
||||
self.name = tempfile.mkdtemp()
|
||||
|
||||
def __enter__(self):
|
||||
return self.name
|
||||
|
||||
def __exit__(self, exctype, excvalue, exctrace):
|
||||
try:
|
||||
shutil.rmtree(self.name, True)
|
||||
except OSError: #removal errors are not the only possible
|
||||
pass
|
||||
self.name = None
|
||||
|
||||
|
||||
unittest_main = unittest.main
|
||||
|
||||
_PY31 = (3, 1) <= sys.version_info[:2] < (3, 2)
|
||||
if _PY31:
|
||||
# on Python 3.1, translate testRunner==None to TextTestRunner
|
||||
# for compatibility with Python 2.6, 2.7, and 3.2+
|
||||
def unittest_main(*args, **kwargs):
|
||||
if 'testRunner' in kwargs and kwargs['testRunner'] is None:
|
||||
kwargs['testRunner'] = unittest.TextTestRunner
|
||||
return unittest.main(*args, **kwargs)
|
||||
-322
@@ -1,322 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import operator
|
||||
import functools
|
||||
import itertools
|
||||
import re
|
||||
|
||||
import 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
|
||||
from pkg_resources import working_set
|
||||
|
||||
from setuptools.compat import builtins, execfile
|
||||
|
||||
__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)
|
||||
# reset to include setup dir, w/clean callback list
|
||||
working_set.__init__()
|
||||
working_set.callbacks.append(lambda dist:dist.activate())
|
||||
DirectorySandbox(setup_dir).run(
|
||||
lambda: execfile(
|
||||
"setup.py",
|
||||
{'__file__':setup_script, '__name__':'__main__'}
|
||||
)
|
||||
)
|
||||
except SystemExit:
|
||||
v = sys.exc_info()[1]
|
||||
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.')
|
||||
]
|
||||
list(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:
|
||||
builtins.file = self._file
|
||||
builtins.open = self._open
|
||||
self._active = True
|
||||
return func()
|
||||
finally:
|
||||
self._active = False
|
||||
if _file:
|
||||
builtins.file = _file
|
||||
builtins.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",
|
||||
])
|
||||
|
||||
_exception_patterns = [
|
||||
# Allow lib2to3 to attempt to save a pickled grammar object (#121)
|
||||
'.*lib2to3.*\.pickle$',
|
||||
]
|
||||
"exempt writing to paths that match the pattern"
|
||||
|
||||
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))
|
||||
return (
|
||||
self._exempted(realpath)
|
||||
or realpath == self._sandbox
|
||||
or realpath.startswith(self._prefix)
|
||||
)
|
||||
finally:
|
||||
self._active = active
|
||||
|
||||
def _exempted(self, filepath):
|
||||
start_matches = (
|
||||
filepath.startswith(exception)
|
||||
for exception in self._exceptions
|
||||
)
|
||||
pattern_matches = (
|
||||
re.match(pattern, filepath)
|
||||
for pattern in self._exception_patterns
|
||||
)
|
||||
candidates = itertools.chain(start_matches, pattern_matches)
|
||||
return any(candidates)
|
||||
|
||||
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=0o777, *args, **kw):
|
||||
"""Called for low-level os.open()"""
|
||||
if flags & WRITE_FLAGS and not self._ok(file):
|
||||
self._violation("os.open", file, flags, mode, *args, **kw)
|
||||
return _os.open(file,flags,mode, *args, **kw)
|
||||
|
||||
WRITE_FLAGS = functools.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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
@@ -1,11 +0,0 @@
|
||||
# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r
|
||||
__requires__ = """%(spec)r"""
|
||||
import sys
|
||||
from pkg_resources import require
|
||||
require("""%(spec)r""")
|
||||
del require
|
||||
__file__ = """%(dev_path)r"""
|
||||
if sys.version_info < (3, 0):
|
||||
execfile(__file__)
|
||||
else:
|
||||
exec(compile(open(__file__).read(), __file__, 'exec'))
|
||||
@@ -1,4 +0,0 @@
|
||||
# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r
|
||||
__requires__ = """%(spec)r"""
|
||||
import pkg_resources
|
||||
pkg_resources.run_script("""%(spec)r""", """%(script_name)r""")
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
def __boot():
|
||||
import sys
|
||||
import os
|
||||
PYTHONPATH = os.environ.get('PYTHONPATH')
|
||||
if PYTHONPATH is None or (sys.platform=='win32' and not PYTHONPATH):
|
||||
PYTHONPATH = []
|
||||
else:
|
||||
PYTHONPATH = PYTHONPATH.split(os.pathsep)
|
||||
|
||||
pic = getattr(sys,'path_importer_cache',{})
|
||||
stdpath = sys.path[len(PYTHONPATH):]
|
||||
mydir = os.path.dirname(__file__)
|
||||
#print "searching",stdpath,sys.path
|
||||
|
||||
for item in stdpath:
|
||||
if item==mydir or not item:
|
||||
continue # skip if current dir. on Windows, or my own directory
|
||||
importer = pic.get(item)
|
||||
if importer is not None:
|
||||
loader = importer.find_module('site')
|
||||
if loader is not None:
|
||||
# This should actually reload the current module
|
||||
loader.load_module('site')
|
||||
break
|
||||
else:
|
||||
try:
|
||||
import imp # Avoid import loop in Python >= 3.3
|
||||
stream, path, descr = imp.find_module('site',[item])
|
||||
except ImportError:
|
||||
continue
|
||||
if stream is None:
|
||||
continue
|
||||
try:
|
||||
# This should actually reload the current module
|
||||
imp.load_module('site',stream,path,descr)
|
||||
finally:
|
||||
stream.close()
|
||||
break
|
||||
else:
|
||||
raise ImportError("Couldn't find the real 'site' module")
|
||||
|
||||
#print "loaded", __file__
|
||||
|
||||
known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp
|
||||
|
||||
oldpos = getattr(sys,'__egginsert',0) # save old insertion position
|
||||
sys.__egginsert = 0 # and reset the current one
|
||||
|
||||
for item in PYTHONPATH:
|
||||
addsitedir(item)
|
||||
|
||||
sys.__egginsert += oldpos # restore effective old position
|
||||
|
||||
d, nd = makepath(stdpath[0])
|
||||
insert_at = None
|
||||
new_path = []
|
||||
|
||||
for item in sys.path:
|
||||
p, np = makepath(item)
|
||||
|
||||
if np==nd and insert_at is None:
|
||||
# We've hit the first 'system' path entry, so added entries go here
|
||||
insert_at = len(new_path)
|
||||
|
||||
if np in known_paths or insert_at is None:
|
||||
new_path.append(item)
|
||||
else:
|
||||
# new path after the insert point, back-insert it
|
||||
new_path.insert(insert_at, item)
|
||||
insert_at += 1
|
||||
|
||||
sys.path[:] = new_path
|
||||
|
||||
if __name__=='site':
|
||||
__boot()
|
||||
del __boot
|
||||
-234
@@ -1,234 +0,0 @@
|
||||
import os
|
||||
import socket
|
||||
import atexit
|
||||
import re
|
||||
|
||||
import pkg_resources
|
||||
from pkg_resources import ResolutionError, ExtractionError
|
||||
from setuptools.compat import urllib2
|
||||
|
||||
try:
|
||||
import ssl
|
||||
except ImportError:
|
||||
ssl = None
|
||||
|
||||
__all__ = [
|
||||
'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths',
|
||||
'opener_for'
|
||||
]
|
||||
|
||||
cert_paths = """
|
||||
/etc/pki/tls/certs/ca-bundle.crt
|
||||
/etc/ssl/certs/ca-certificates.crt
|
||||
/usr/share/ssl/certs/ca-bundle.crt
|
||||
/usr/local/share/certs/ca-root.crt
|
||||
/etc/ssl/cert.pem
|
||||
/System/Library/OpenSSL/certs/cert.pem
|
||||
""".strip().split()
|
||||
|
||||
|
||||
HTTPSHandler = HTTPSConnection = object
|
||||
|
||||
for what, where in (
|
||||
('HTTPSHandler', ['urllib2','urllib.request']),
|
||||
('HTTPSConnection', ['httplib', 'http.client']),
|
||||
):
|
||||
for module in where:
|
||||
try:
|
||||
exec("from %s import %s" % (module, what))
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection)
|
||||
|
||||
|
||||
try:
|
||||
from ssl import CertificateError, match_hostname
|
||||
except ImportError:
|
||||
try:
|
||||
from backports.ssl_match_hostname import CertificateError
|
||||
from backports.ssl_match_hostname import match_hostname
|
||||
except ImportError:
|
||||
CertificateError = None
|
||||
match_hostname = None
|
||||
|
||||
if not CertificateError:
|
||||
class CertificateError(ValueError):
|
||||
pass
|
||||
|
||||
if not match_hostname:
|
||||
def _dnsname_match(dn, hostname, max_wildcards=1):
|
||||
"""Matching according to RFC 6125, section 6.4.3
|
||||
|
||||
http://tools.ietf.org/html/rfc6125#section-6.4.3
|
||||
"""
|
||||
pats = []
|
||||
if not dn:
|
||||
return False
|
||||
|
||||
# Ported from python3-syntax:
|
||||
# leftmost, *remainder = dn.split(r'.')
|
||||
parts = dn.split(r'.')
|
||||
leftmost = parts[0]
|
||||
remainder = parts[1:]
|
||||
|
||||
wildcards = leftmost.count('*')
|
||||
if wildcards > max_wildcards:
|
||||
# Issue #17980: avoid denials of service by refusing more
|
||||
# than one wildcard per fragment. A survey of established
|
||||
# policy among SSL implementations showed it to be a
|
||||
# reasonable choice.
|
||||
raise CertificateError(
|
||||
"too many wildcards in certificate DNS name: " + repr(dn))
|
||||
|
||||
# speed up common case w/o wildcards
|
||||
if not wildcards:
|
||||
return dn.lower() == hostname.lower()
|
||||
|
||||
# RFC 6125, section 6.4.3, subitem 1.
|
||||
# The client SHOULD NOT attempt to match a presented identifier in which
|
||||
# the wildcard character comprises a label other than the left-most label.
|
||||
if leftmost == '*':
|
||||
# When '*' is a fragment by itself, it matches a non-empty dotless
|
||||
# fragment.
|
||||
pats.append('[^.]+')
|
||||
elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
|
||||
# RFC 6125, section 6.4.3, subitem 3.
|
||||
# The client SHOULD NOT attempt to match a presented identifier
|
||||
# where the wildcard character is embedded within an A-label or
|
||||
# U-label of an internationalized domain name.
|
||||
pats.append(re.escape(leftmost))
|
||||
else:
|
||||
# Otherwise, '*' matches any dotless string, e.g. www*
|
||||
pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
|
||||
|
||||
# add the remaining fragments, ignore any wildcards
|
||||
for frag in remainder:
|
||||
pats.append(re.escape(frag))
|
||||
|
||||
pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
|
||||
return pat.match(hostname)
|
||||
|
||||
def match_hostname(cert, hostname):
|
||||
"""Verify that *cert* (in decoded format as returned by
|
||||
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
|
||||
rules are followed, but IP addresses are not accepted for *hostname*.
|
||||
|
||||
CertificateError is raised on failure. On success, the function
|
||||
returns nothing.
|
||||
"""
|
||||
if not cert:
|
||||
raise ValueError("empty or no certificate")
|
||||
dnsnames = []
|
||||
san = cert.get('subjectAltName', ())
|
||||
for key, value in san:
|
||||
if key == 'DNS':
|
||||
if _dnsname_match(value, hostname):
|
||||
return
|
||||
dnsnames.append(value)
|
||||
if not dnsnames:
|
||||
# The subject is only checked when there is no dNSName entry
|
||||
# in subjectAltName
|
||||
for sub in cert.get('subject', ()):
|
||||
for key, value in sub:
|
||||
# XXX according to RFC 2818, the most specific Common Name
|
||||
# must be used.
|
||||
if key == 'commonName':
|
||||
if _dnsname_match(value, hostname):
|
||||
return
|
||||
dnsnames.append(value)
|
||||
if len(dnsnames) > 1:
|
||||
raise CertificateError("hostname %r "
|
||||
"doesn't match either of %s"
|
||||
% (hostname, ', '.join(map(repr, dnsnames))))
|
||||
elif len(dnsnames) == 1:
|
||||
raise CertificateError("hostname %r "
|
||||
"doesn't match %r"
|
||||
% (hostname, dnsnames[0]))
|
||||
else:
|
||||
raise CertificateError("no appropriate commonName or "
|
||||
"subjectAltName fields were found")
|
||||
|
||||
|
||||
class VerifyingHTTPSHandler(HTTPSHandler):
|
||||
"""Simple verifying handler: no auth, subclasses, timeouts, etc."""
|
||||
|
||||
def __init__(self, ca_bundle):
|
||||
self.ca_bundle = ca_bundle
|
||||
HTTPSHandler.__init__(self)
|
||||
|
||||
def https_open(self, req):
|
||||
return self.do_open(
|
||||
lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw), req
|
||||
)
|
||||
|
||||
|
||||
class VerifyingHTTPSConn(HTTPSConnection):
|
||||
"""Simple verifying connection: no auth, subclasses, timeouts, etc."""
|
||||
def __init__(self, host, ca_bundle, **kw):
|
||||
HTTPSConnection.__init__(self, host, **kw)
|
||||
self.ca_bundle = ca_bundle
|
||||
|
||||
def connect(self):
|
||||
sock = socket.create_connection(
|
||||
(self.host, self.port), getattr(self, 'source_address', None)
|
||||
)
|
||||
|
||||
# Handle the socket if a (proxy) tunnel is present
|
||||
if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None):
|
||||
self.sock = sock
|
||||
self._tunnel()
|
||||
|
||||
self.sock = ssl.wrap_socket(
|
||||
sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle
|
||||
)
|
||||
try:
|
||||
match_hostname(self.sock.getpeercert(), self.host)
|
||||
except CertificateError:
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
self.sock.close()
|
||||
raise
|
||||
|
||||
def opener_for(ca_bundle=None):
|
||||
"""Get a urlopen() replacement that uses ca_bundle for verification"""
|
||||
return urllib2.build_opener(
|
||||
VerifyingHTTPSHandler(ca_bundle or find_ca_bundle())
|
||||
).open
|
||||
|
||||
|
||||
_wincerts = None
|
||||
|
||||
def get_win_certfile():
|
||||
global _wincerts
|
||||
if _wincerts is not None:
|
||||
return _wincerts.name
|
||||
|
||||
try:
|
||||
from wincertstore import CertFile
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
class MyCertFile(CertFile):
|
||||
def __init__(self, stores=(), certs=()):
|
||||
CertFile.__init__(self)
|
||||
for store in stores:
|
||||
self.addstore(store)
|
||||
self.addcerts(certs)
|
||||
atexit.register(self.close)
|
||||
|
||||
_wincerts = MyCertFile(stores=['CA', 'ROOT'])
|
||||
return _wincerts.name
|
||||
|
||||
|
||||
def find_ca_bundle():
|
||||
"""Return an existing CA bundle path, or None"""
|
||||
if os.name=='nt':
|
||||
return get_win_certfile()
|
||||
else:
|
||||
for cert_path in cert_paths:
|
||||
if os.path.isfile(cert_path):
|
||||
return cert_path
|
||||
try:
|
||||
return pkg_resources.resource_filename('certifi', 'cacert.pem')
|
||||
except (ImportError, ResolutionError, ExtractionError):
|
||||
return None
|
||||
-583
@@ -1,583 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from distutils import log
|
||||
import xml.dom.pulldom
|
||||
import shlex
|
||||
import locale
|
||||
import codecs
|
||||
import unicodedata
|
||||
import warnings
|
||||
from setuptools.compat import unicode
|
||||
from setuptools.py31compat import TemporaryDirectory
|
||||
from xml.sax.saxutils import unescape
|
||||
|
||||
try:
|
||||
import urlparse
|
||||
except ImportError:
|
||||
import urllib.parse as urlparse
|
||||
|
||||
from subprocess import Popen as _Popen, PIPE as _PIPE
|
||||
|
||||
#NOTE: Use of the command line options require SVN 1.3 or newer (December 2005)
|
||||
# and SVN 1.3 hasn't been supported by the developers since mid 2008.
|
||||
|
||||
#subprocess is called several times with shell=(sys.platform=='win32')
|
||||
#see the follow for more information:
|
||||
# http://bugs.python.org/issue8557
|
||||
# http://stackoverflow.com/questions/5658622/
|
||||
# python-subprocess-popen-environment-path
|
||||
|
||||
def _run_command(args, stdout=_PIPE, stderr=_PIPE, encoding=None, stream=0):
|
||||
#regarding the shell argument, see: http://bugs.python.org/issue8557
|
||||
try:
|
||||
proc = _Popen(args, stdout=stdout, stderr=stderr,
|
||||
shell=(sys.platform == 'win32'))
|
||||
|
||||
data = proc.communicate()[stream]
|
||||
except OSError:
|
||||
return 1, ''
|
||||
|
||||
#doubled checked and
|
||||
data = decode_as_string(data, encoding)
|
||||
|
||||
#communciate calls wait()
|
||||
return proc.returncode, data
|
||||
|
||||
|
||||
def _get_entry_schedule(entry):
|
||||
schedule = entry.getElementsByTagName('schedule')[0]
|
||||
return "".join([t.nodeValue
|
||||
for t in schedule.childNodes
|
||||
if t.nodeType == t.TEXT_NODE])
|
||||
|
||||
|
||||
def _get_target_property(target):
|
||||
property_text = target.getElementsByTagName('property')[0]
|
||||
return "".join([t.nodeValue
|
||||
for t in property_text.childNodes
|
||||
if t.nodeType == t.TEXT_NODE])
|
||||
|
||||
|
||||
def _get_xml_data(decoded_str):
|
||||
if sys.version_info < (3, 0):
|
||||
#old versions want an encoded string
|
||||
data = decoded_str.encode('utf-8')
|
||||
else:
|
||||
data = decoded_str
|
||||
return data
|
||||
|
||||
|
||||
def joinpath(prefix, *suffix):
|
||||
if not prefix or prefix == '.':
|
||||
return os.path.join(*suffix)
|
||||
return os.path.join(prefix, *suffix)
|
||||
|
||||
def determine_console_encoding():
|
||||
try:
|
||||
#try for the preferred encoding
|
||||
encoding = locale.getpreferredencoding()
|
||||
|
||||
#see if the locale.getdefaultlocale returns null
|
||||
#some versions of python\platforms return US-ASCII
|
||||
#when it cannot determine an encoding
|
||||
if not encoding or encoding == "US-ASCII":
|
||||
encoding = locale.getdefaultlocale()[1]
|
||||
|
||||
if encoding:
|
||||
codecs.lookup(encoding) # make sure a lookup error is not made
|
||||
|
||||
except (locale.Error, LookupError):
|
||||
encoding = None
|
||||
|
||||
is_osx = sys.platform == "darwin"
|
||||
if not encoding:
|
||||
return ["US-ASCII", "utf-8"][is_osx]
|
||||
elif encoding.startswith("mac-") and is_osx:
|
||||
#certain versions of python would return mac-roman as default
|
||||
#OSX as a left over of earlier mac versions.
|
||||
return "utf-8"
|
||||
else:
|
||||
return encoding
|
||||
|
||||
_console_encoding = determine_console_encoding()
|
||||
|
||||
def decode_as_string(text, encoding=None):
|
||||
"""
|
||||
Decode the console or file output explicitly using getpreferredencoding.
|
||||
The text paraemeter should be a encoded string, if not no decode occurs
|
||||
If no encoding is given, getpreferredencoding is used. If encoding is
|
||||
specified, that is used instead. This would be needed for SVN --xml
|
||||
output. Unicode is explicitly put in composed NFC form.
|
||||
|
||||
--xml should be UTF-8 (SVN Issue 2938) the discussion on the Subversion
|
||||
DEV List from 2007 seems to indicate the same.
|
||||
"""
|
||||
#text should be a byte string
|
||||
|
||||
if encoding is None:
|
||||
encoding = _console_encoding
|
||||
|
||||
if not isinstance(text, unicode):
|
||||
text = text.decode(encoding)
|
||||
|
||||
text = unicodedata.normalize('NFC', text)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def parse_dir_entries(decoded_str):
|
||||
'''Parse the entries from a recursive info xml'''
|
||||
doc = xml.dom.pulldom.parseString(_get_xml_data(decoded_str))
|
||||
entries = list()
|
||||
|
||||
for event, node in doc:
|
||||
if event == 'START_ELEMENT' and node.nodeName == 'entry':
|
||||
doc.expandNode(node)
|
||||
if not _get_entry_schedule(node).startswith('delete'):
|
||||
entries.append((node.getAttribute('path'),
|
||||
node.getAttribute('kind')))
|
||||
|
||||
return entries[1:] # do not want the root directory
|
||||
|
||||
|
||||
def parse_externals_xml(decoded_str, prefix=''):
|
||||
'''Parse a propget svn:externals xml'''
|
||||
prefix = os.path.normpath(prefix)
|
||||
prefix = os.path.normcase(prefix)
|
||||
|
||||
doc = xml.dom.pulldom.parseString(_get_xml_data(decoded_str))
|
||||
externals = list()
|
||||
|
||||
for event, node in doc:
|
||||
if event == 'START_ELEMENT' and node.nodeName == 'target':
|
||||
doc.expandNode(node)
|
||||
path = os.path.normpath(node.getAttribute('path'))
|
||||
|
||||
if os.path.normcase(path).startswith(prefix):
|
||||
path = path[len(prefix)+1:]
|
||||
|
||||
data = _get_target_property(node)
|
||||
#data should be decoded already
|
||||
for external in parse_external_prop(data):
|
||||
externals.append(joinpath(path, external))
|
||||
|
||||
return externals # do not want the root directory
|
||||
|
||||
|
||||
def parse_external_prop(lines):
|
||||
"""
|
||||
Parse the value of a retrieved svn:externals entry.
|
||||
|
||||
possible token setups (with quotng and backscaping in laters versions)
|
||||
URL[@#] EXT_FOLDERNAME
|
||||
[-r#] URL EXT_FOLDERNAME
|
||||
EXT_FOLDERNAME [-r#] URL
|
||||
"""
|
||||
externals = []
|
||||
for line in lines.splitlines():
|
||||
line = line.lstrip() # there might be a "\ "
|
||||
if not line:
|
||||
continue
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
#shlex handles NULLs just fine and shlex in 2.7 tries to encode
|
||||
#as ascii automatiically
|
||||
line = line.encode('utf-8')
|
||||
line = shlex.split(line)
|
||||
if sys.version_info < (3, 0):
|
||||
line = [x.decode('utf-8') for x in line]
|
||||
|
||||
#EXT_FOLDERNAME is either the first or last depending on where
|
||||
#the URL falls
|
||||
if urlparse.urlsplit(line[-1])[0]:
|
||||
external = line[0]
|
||||
else:
|
||||
external = line[-1]
|
||||
|
||||
external = decode_as_string(external, encoding="utf-8")
|
||||
externals.append(os.path.normpath(external))
|
||||
|
||||
return externals
|
||||
|
||||
|
||||
def parse_prop_file(filename, key):
|
||||
found = False
|
||||
f = open(filename, 'rt')
|
||||
data = ''
|
||||
try:
|
||||
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 == key:
|
||||
found = True
|
||||
elif kind == 'V' and found:
|
||||
break
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class SvnInfo(object):
|
||||
'''
|
||||
Generic svn_info object. No has little knowledge of how to extract
|
||||
information. Use cls.load to instatiate according svn version.
|
||||
|
||||
Paths are not filesystem encoded.
|
||||
'''
|
||||
|
||||
@staticmethod
|
||||
def get_svn_version():
|
||||
# Temp config directory should be enough to check for repository
|
||||
# This is needed because .svn always creates .subversion and
|
||||
# some operating systems do not handle dot directory correctly.
|
||||
# Real queries in real svn repos with be concerned with it creation
|
||||
with TemporaryDirectory() as tempdir:
|
||||
code, data = _run_command(['svn',
|
||||
'--config-dir', tempdir,
|
||||
'--version',
|
||||
'--quiet'])
|
||||
|
||||
if code == 0 and data:
|
||||
return data.strip()
|
||||
else:
|
||||
return ''
|
||||
|
||||
#svnversion return values (previous implementations return max revision)
|
||||
# 4123:4168 mixed revision working copy
|
||||
# 4168M modified working copy
|
||||
# 4123S switched working copy
|
||||
# 4123:4168MS mixed revision, modified, switched working copy
|
||||
revision_re = re.compile(r'(?:([\-0-9]+):)?(\d+)([a-z]*)\s*$', re.I)
|
||||
|
||||
@classmethod
|
||||
def load(cls, dirname=''):
|
||||
normdir = os.path.normpath(dirname)
|
||||
|
||||
# Temp config directory should be enough to check for repository
|
||||
# This is needed because .svn always creates .subversion and
|
||||
# some operating systems do not handle dot directory correctly.
|
||||
# Real queries in real svn repos with be concerned with it creation
|
||||
with TemporaryDirectory() as tempdir:
|
||||
code, data = _run_command(['svn',
|
||||
'--config-dir', tempdir,
|
||||
'info', normdir])
|
||||
|
||||
# Must check for some contents, as some use empty directories
|
||||
# in testcases, however only enteries is needed also the info
|
||||
# command above MUST have worked
|
||||
svn_dir = os.path.join(normdir, '.svn')
|
||||
is_svn_wd = (not code or
|
||||
os.path.isfile(os.path.join(svn_dir, 'entries')))
|
||||
|
||||
svn_version = tuple(cls.get_svn_version().split('.'))
|
||||
|
||||
try:
|
||||
base_svn_version = tuple(int(x) for x in svn_version[:2])
|
||||
except ValueError:
|
||||
base_svn_version = tuple()
|
||||
|
||||
if not is_svn_wd:
|
||||
#return an instance of this NO-OP class
|
||||
return SvnInfo(dirname)
|
||||
|
||||
if code or not base_svn_version or base_svn_version < (1, 3):
|
||||
warnings.warn(("No SVN 1.3+ command found: falling back "
|
||||
"on pre 1.7 .svn parsing"), DeprecationWarning)
|
||||
return SvnFileInfo(dirname)
|
||||
|
||||
if base_svn_version < (1, 5):
|
||||
return Svn13Info(dirname)
|
||||
|
||||
return Svn15Info(dirname)
|
||||
|
||||
def __init__(self, path=''):
|
||||
self.path = path
|
||||
self._entries = None
|
||||
self._externals = None
|
||||
|
||||
def get_revision(self):
|
||||
'Retrieve the directory revision informatino using svnversion'
|
||||
code, data = _run_command(['svnversion', '-c', self.path])
|
||||
if code:
|
||||
log.warn("svnversion failed")
|
||||
return 0
|
||||
|
||||
parsed = self.revision_re.match(data)
|
||||
if parsed:
|
||||
return int(parsed.group(2))
|
||||
else:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def entries(self):
|
||||
if self._entries is None:
|
||||
self._entries = self.get_entries()
|
||||
return self._entries
|
||||
|
||||
@property
|
||||
def externals(self):
|
||||
if self._externals is None:
|
||||
self._externals = self.get_externals()
|
||||
return self._externals
|
||||
|
||||
def iter_externals(self):
|
||||
'''
|
||||
Iterate over the svn:external references in the repository path.
|
||||
'''
|
||||
for item in self.externals:
|
||||
yield item
|
||||
|
||||
def iter_files(self):
|
||||
'''
|
||||
Iterate over the non-deleted file entries in the repository path
|
||||
'''
|
||||
for item, kind in self.entries:
|
||||
if kind.lower() == 'file':
|
||||
yield item
|
||||
|
||||
def iter_dirs(self, include_root=True):
|
||||
'''
|
||||
Iterate over the non-deleted file entries in the repository path
|
||||
'''
|
||||
if include_root:
|
||||
yield self.path
|
||||
for item, kind in self.entries:
|
||||
if kind.lower() == 'dir':
|
||||
yield item
|
||||
|
||||
def get_entries(self):
|
||||
return []
|
||||
|
||||
def get_externals(self):
|
||||
return []
|
||||
|
||||
|
||||
class Svn13Info(SvnInfo):
|
||||
def get_entries(self):
|
||||
code, data = _run_command(['svn', 'info', '-R', '--xml', self.path],
|
||||
encoding="utf-8")
|
||||
|
||||
if code:
|
||||
log.debug("svn info failed")
|
||||
return []
|
||||
|
||||
return parse_dir_entries(data)
|
||||
|
||||
def get_externals(self):
|
||||
#Previous to 1.5 --xml was not supported for svn propget and the -R
|
||||
#output format breaks the shlex compatible semantics.
|
||||
cmd = ['svn', 'propget', 'svn:externals']
|
||||
result = []
|
||||
for folder in self.iter_dirs():
|
||||
code, lines = _run_command(cmd + [folder], encoding="utf-8")
|
||||
if code != 0:
|
||||
log.warn("svn propget failed")
|
||||
return []
|
||||
#lines should a str
|
||||
for external in parse_external_prop(lines):
|
||||
if folder:
|
||||
external = os.path.join(folder, external)
|
||||
result.append(os.path.normpath(external))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Svn15Info(Svn13Info):
|
||||
def get_externals(self):
|
||||
cmd = ['svn', 'propget', 'svn:externals', self.path, '-R', '--xml']
|
||||
code, lines = _run_command(cmd, encoding="utf-8")
|
||||
if code:
|
||||
log.debug("svn propget failed")
|
||||
return []
|
||||
return parse_externals_xml(lines, prefix=os.path.abspath(self.path))
|
||||
|
||||
|
||||
class SvnFileInfo(SvnInfo):
|
||||
|
||||
def __init__(self, path=''):
|
||||
super(SvnFileInfo, self).__init__(path)
|
||||
self._directories = None
|
||||
self._revision = None
|
||||
|
||||
def _walk_svn(self, base):
|
||||
entry_file = joinpath(base, '.svn', 'entries')
|
||||
if os.path.isfile(entry_file):
|
||||
entries = SVNEntriesFile.load(base)
|
||||
yield (base, False, entries.parse_revision())
|
||||
for path in entries.get_undeleted_records():
|
||||
path = decode_as_string(path)
|
||||
path = joinpath(base, path)
|
||||
if os.path.isfile(path):
|
||||
yield (path, True, None)
|
||||
elif os.path.isdir(path):
|
||||
for item in self._walk_svn(path):
|
||||
yield item
|
||||
|
||||
def _build_entries(self):
|
||||
entries = list()
|
||||
|
||||
rev = 0
|
||||
for path, isfile, dir_rev in self._walk_svn(self.path):
|
||||
if isfile:
|
||||
entries.append((path, 'file'))
|
||||
else:
|
||||
entries.append((path, 'dir'))
|
||||
rev = max(rev, dir_rev)
|
||||
|
||||
self._entries = entries
|
||||
self._revision = rev
|
||||
|
||||
def get_entries(self):
|
||||
if self._entries is None:
|
||||
self._build_entries()
|
||||
return self._entries
|
||||
|
||||
def get_revision(self):
|
||||
if self._revision is None:
|
||||
self._build_entries()
|
||||
return self._revision
|
||||
|
||||
def get_externals(self):
|
||||
prop_files = [['.svn', 'dir-prop-base'],
|
||||
['.svn', 'dir-props']]
|
||||
externals = []
|
||||
|
||||
for dirname in self.iter_dirs():
|
||||
prop_file = None
|
||||
for rel_parts in prop_files:
|
||||
filename = joinpath(dirname, *rel_parts)
|
||||
if os.path.isfile(filename):
|
||||
prop_file = filename
|
||||
|
||||
if prop_file is not None:
|
||||
ext_prop = parse_prop_file(prop_file, 'svn:externals')
|
||||
#ext_prop should be utf-8 coming from svn:externals
|
||||
ext_prop = decode_as_string(ext_prop, encoding="utf-8")
|
||||
externals.extend(parse_external_prop(ext_prop))
|
||||
|
||||
return externals
|
||||
|
||||
|
||||
def svn_finder(dirname=''):
|
||||
#combined externals due to common interface
|
||||
#combined externals and entries due to lack of dir_props in 1.7
|
||||
info = SvnInfo.load(dirname)
|
||||
for path in info.iter_files():
|
||||
yield path
|
||||
|
||||
for path in info.iter_externals():
|
||||
sub_info = SvnInfo.load(path)
|
||||
for sub_path in sub_info.iter_files():
|
||||
yield sub_path
|
||||
|
||||
|
||||
class SVNEntriesFile(object):
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
@classmethod
|
||||
def load(class_, base):
|
||||
filename = os.path.join(base, '.svn', 'entries')
|
||||
f = open(filename)
|
||||
try:
|
||||
result = SVNEntriesFile.read(f)
|
||||
finally:
|
||||
f.close()
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def read(class_, fileobj):
|
||||
data = fileobj.read()
|
||||
is_xml = data.startswith('<?xml')
|
||||
class_ = [SVNEntriesFileText, SVNEntriesFileXML][is_xml]
|
||||
return class_(data)
|
||||
|
||||
def parse_revision(self):
|
||||
all_revs = self.parse_revision_numbers() + [0]
|
||||
return max(all_revs)
|
||||
|
||||
|
||||
class SVNEntriesFileText(SVNEntriesFile):
|
||||
known_svn_versions = {
|
||||
'1.4.x': 8,
|
||||
'1.5.x': 9,
|
||||
'1.6.x': 10,
|
||||
}
|
||||
|
||||
def __get_cached_sections(self):
|
||||
return self.sections
|
||||
|
||||
def get_sections(self):
|
||||
SECTION_DIVIDER = '\f\n'
|
||||
sections = self.data.split(SECTION_DIVIDER)
|
||||
sections = [x for x in map(str.splitlines, sections)]
|
||||
try:
|
||||
# remove the SVN version number from the first line
|
||||
svn_version = int(sections[0].pop(0))
|
||||
if not svn_version in self.known_svn_versions.values():
|
||||
log.warn("Unknown subversion verson %d", svn_version)
|
||||
except ValueError:
|
||||
return
|
||||
self.sections = sections
|
||||
self.get_sections = self.__get_cached_sections
|
||||
return self.sections
|
||||
|
||||
def is_valid(self):
|
||||
return bool(self.get_sections())
|
||||
|
||||
def get_url(self):
|
||||
return self.get_sections()[0][4]
|
||||
|
||||
def parse_revision_numbers(self):
|
||||
revision_line_number = 9
|
||||
rev_numbers = [
|
||||
int(section[revision_line_number])
|
||||
for section in self.get_sections()
|
||||
if (len(section) > revision_line_number
|
||||
and section[revision_line_number])
|
||||
]
|
||||
return rev_numbers
|
||||
|
||||
def get_undeleted_records(self):
|
||||
undeleted = lambda s: s and s[0] and (len(s) < 6 or s[5] != 'delete')
|
||||
result = [
|
||||
section[0]
|
||||
for section in self.get_sections()
|
||||
if undeleted(section)
|
||||
]
|
||||
return result
|
||||
|
||||
|
||||
class SVNEntriesFileXML(SVNEntriesFile):
|
||||
def is_valid(self):
|
||||
return True
|
||||
|
||||
def get_url(self):
|
||||
"Get repository URL"
|
||||
urlre = re.compile('url="([^"]+)"')
|
||||
return urlre.search(self.data).group(1)
|
||||
|
||||
def parse_revision_numbers(self):
|
||||
revre = re.compile(r'committed-rev="(\d+)"')
|
||||
return [
|
||||
int(m.group(1))
|
||||
for m in revre.finditer(self.data)
|
||||
]
|
||||
|
||||
def get_undeleted_records(self):
|
||||
entries_pattern = \
|
||||
re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I)
|
||||
results = [
|
||||
unescape(match.group(1))
|
||||
for match in entries_pattern.finditer(self.data)
|
||||
]
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
for name in svn_finder(sys.argv[1]):
|
||||
print(name)
|
||||
-352
@@ -1,352 +0,0 @@
|
||||
"""Tests for the 'setuptools' package"""
|
||||
import sys
|
||||
import os
|
||||
import unittest
|
||||
from setuptools.tests import doctest
|
||||
import distutils.core
|
||||
import distutils.cmd
|
||||
from distutils.errors import DistutilsOptionError, DistutilsPlatformError
|
||||
from distutils.errors import DistutilsSetupError
|
||||
from distutils.core import Extension
|
||||
from distutils.version import LooseVersion
|
||||
from setuptools.compat import func_code
|
||||
|
||||
from setuptools.compat import func_code
|
||||
import setuptools.dist
|
||||
import setuptools.depends as dep
|
||||
from setuptools import Feature
|
||||
from setuptools.depends import Require
|
||||
|
||||
def additional_tests():
|
||||
import doctest, unittest
|
||||
suite = unittest.TestSuite((
|
||||
doctest.DocFileSuite(
|
||||
os.path.join('tests', 'api_tests.txt'),
|
||||
optionflags=doctest.ELLIPSIS, package='pkg_resources',
|
||||
),
|
||||
))
|
||||
if sys.platform == 'win32':
|
||||
suite.addTest(doctest.DocFileSuite('win_script_wrapper.txt'))
|
||||
return suite
|
||||
|
||||
def makeSetup(**args):
|
||||
"""Return distribution from 'setup(**args)', without executing commands"""
|
||||
|
||||
distutils.core._setup_stop_after = "commandline"
|
||||
|
||||
# Don't let system command line leak into tests!
|
||||
args.setdefault('script_args',['install'])
|
||||
|
||||
try:
|
||||
return setuptools.setup(**args)
|
||||
finally:
|
||||
distutils.core._setup_stop_after = None
|
||||
|
||||
|
||||
class DependsTests(unittest.TestCase):
|
||||
|
||||
def testExtractConst(self):
|
||||
if not hasattr(dep, 'extract_constant'):
|
||||
# skip on non-bytecode platforms
|
||||
return
|
||||
|
||||
def f1():
|
||||
global x, y, z
|
||||
x = "test"
|
||||
y = z
|
||||
|
||||
fc = func_code(f1)
|
||||
# unrecognized name
|
||||
self.assertEqual(dep.extract_constant(fc,'q', -1), None)
|
||||
|
||||
# constant assigned
|
||||
self.assertEqual(dep.extract_constant(fc,'x', -1), "test")
|
||||
|
||||
# expression assigned
|
||||
self.assertEqual(dep.extract_constant(fc,'y', -1), -1)
|
||||
|
||||
# recognized name, not assigned
|
||||
self.assertEqual(dep.extract_constant(fc,'z', -1), None)
|
||||
|
||||
def testFindModule(self):
|
||||
self.assertRaises(ImportError, dep.find_module, 'no-such.-thing')
|
||||
self.assertRaises(ImportError, dep.find_module, 'setuptools.non-existent')
|
||||
f,p,i = dep.find_module('setuptools.tests')
|
||||
f.close()
|
||||
|
||||
def testModuleExtract(self):
|
||||
if not hasattr(dep, 'get_module_constant'):
|
||||
# skip on non-bytecode platforms
|
||||
return
|
||||
|
||||
from email import __version__
|
||||
self.assertEqual(
|
||||
dep.get_module_constant('email','__version__'), __version__
|
||||
)
|
||||
self.assertEqual(
|
||||
dep.get_module_constant('sys','version'), sys.version
|
||||
)
|
||||
self.assertEqual(
|
||||
dep.get_module_constant('setuptools.tests','__doc__'),__doc__
|
||||
)
|
||||
|
||||
def testRequire(self):
|
||||
if not hasattr(dep, 'extract_constant'):
|
||||
# skip on non-bytecode platformsh
|
||||
return
|
||||
|
||||
req = Require('Email','1.0.3','email')
|
||||
|
||||
self.assertEqual(req.name, 'Email')
|
||||
self.assertEqual(req.module, 'email')
|
||||
self.assertEqual(req.requested_version, '1.0.3')
|
||||
self.assertEqual(req.attribute, '__version__')
|
||||
self.assertEqual(req.full_name(), 'Email-1.0.3')
|
||||
|
||||
from email import __version__
|
||||
self.assertEqual(req.get_version(), __version__)
|
||||
self.assertTrue(req.version_ok('1.0.9'))
|
||||
self.assertTrue(not req.version_ok('0.9.1'))
|
||||
self.assertTrue(not req.version_ok('unknown'))
|
||||
|
||||
self.assertTrue(req.is_present())
|
||||
self.assertTrue(req.is_current())
|
||||
|
||||
req = Require('Email 3000','03000','email',format=LooseVersion)
|
||||
self.assertTrue(req.is_present())
|
||||
self.assertTrue(not req.is_current())
|
||||
self.assertTrue(not req.version_ok('unknown'))
|
||||
|
||||
req = Require('Do-what-I-mean','1.0','d-w-i-m')
|
||||
self.assertTrue(not req.is_present())
|
||||
self.assertTrue(not req.is_current())
|
||||
|
||||
req = Require('Tests', None, 'tests', homepage="http://example.com")
|
||||
self.assertEqual(req.format, None)
|
||||
self.assertEqual(req.attribute, None)
|
||||
self.assertEqual(req.requested_version, None)
|
||||
self.assertEqual(req.full_name(), 'Tests')
|
||||
self.assertEqual(req.homepage, 'http://example.com')
|
||||
|
||||
paths = [os.path.dirname(p) for p in __path__]
|
||||
self.assertTrue(req.is_present(paths))
|
||||
self.assertTrue(req.is_current(paths))
|
||||
|
||||
|
||||
class DistroTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.e1 = Extension('bar.ext',['bar.c'])
|
||||
self.e2 = Extension('c.y', ['y.c'])
|
||||
|
||||
self.dist = makeSetup(
|
||||
packages=['a', 'a.b', 'a.b.c', 'b', 'c'],
|
||||
py_modules=['b.d','x'],
|
||||
ext_modules = (self.e1, self.e2),
|
||||
package_dir = {},
|
||||
)
|
||||
|
||||
def testDistroType(self):
|
||||
self.assertTrue(isinstance(self.dist,setuptools.dist.Distribution))
|
||||
|
||||
def testExcludePackage(self):
|
||||
self.dist.exclude_package('a')
|
||||
self.assertEqual(self.dist.packages, ['b','c'])
|
||||
|
||||
self.dist.exclude_package('b')
|
||||
self.assertEqual(self.dist.packages, ['c'])
|
||||
self.assertEqual(self.dist.py_modules, ['x'])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e1, self.e2])
|
||||
|
||||
self.dist.exclude_package('c')
|
||||
self.assertEqual(self.dist.packages, [])
|
||||
self.assertEqual(self.dist.py_modules, ['x'])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e1])
|
||||
|
||||
# test removals from unspecified options
|
||||
makeSetup().exclude_package('x')
|
||||
|
||||
def testIncludeExclude(self):
|
||||
# remove an extension
|
||||
self.dist.exclude(ext_modules=[self.e1])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e2])
|
||||
|
||||
# add it back in
|
||||
self.dist.include(ext_modules=[self.e1])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e2, self.e1])
|
||||
|
||||
# should not add duplicate
|
||||
self.dist.include(ext_modules=[self.e1])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e2, self.e1])
|
||||
|
||||
def testExcludePackages(self):
|
||||
self.dist.exclude(packages=['c','b','a'])
|
||||
self.assertEqual(self.dist.packages, [])
|
||||
self.assertEqual(self.dist.py_modules, ['x'])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e1])
|
||||
|
||||
def testEmpty(self):
|
||||
dist = makeSetup()
|
||||
dist.include(packages=['a'], py_modules=['b'], ext_modules=[self.e2])
|
||||
dist = makeSetup()
|
||||
dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2])
|
||||
|
||||
def testContents(self):
|
||||
self.assertTrue(self.dist.has_contents_for('a'))
|
||||
self.dist.exclude_package('a')
|
||||
self.assertTrue(not self.dist.has_contents_for('a'))
|
||||
|
||||
self.assertTrue(self.dist.has_contents_for('b'))
|
||||
self.dist.exclude_package('b')
|
||||
self.assertTrue(not self.dist.has_contents_for('b'))
|
||||
|
||||
self.assertTrue(self.dist.has_contents_for('c'))
|
||||
self.dist.exclude_package('c')
|
||||
self.assertTrue(not self.dist.has_contents_for('c'))
|
||||
|
||||
def testInvalidIncludeExclude(self):
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.include, nonexistent_option='x'
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.exclude, nonexistent_option='x'
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.include, packages={'x':'y'}
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.exclude, packages={'x':'y'}
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.include, ext_modules={'x':'y'}
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.exclude, ext_modules={'x':'y'}
|
||||
)
|
||||
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.include, package_dir=['q']
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.exclude, package_dir=['q']
|
||||
)
|
||||
|
||||
|
||||
class FeatureTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.req = Require('Distutils','1.0.3','distutils')
|
||||
self.dist = makeSetup(
|
||||
features={
|
||||
'foo': Feature("foo",standard=True,require_features=['baz',self.req]),
|
||||
'bar': Feature("bar", standard=True, packages=['pkg.bar'],
|
||||
py_modules=['bar_et'], remove=['bar.ext'],
|
||||
),
|
||||
'baz': Feature(
|
||||
"baz", optional=False, packages=['pkg.baz'],
|
||||
scripts = ['scripts/baz_it'],
|
||||
libraries=[('libfoo','foo/foofoo.c')]
|
||||
),
|
||||
'dwim': Feature("DWIM", available=False, remove='bazish'),
|
||||
},
|
||||
script_args=['--without-bar', 'install'],
|
||||
packages = ['pkg.bar', 'pkg.foo'],
|
||||
py_modules = ['bar_et', 'bazish'],
|
||||
ext_modules = [Extension('bar.ext',['bar.c'])]
|
||||
)
|
||||
|
||||
def testDefaults(self):
|
||||
self.assertTrue(not
|
||||
Feature(
|
||||
"test",standard=True,remove='x',available=False
|
||||
).include_by_default()
|
||||
)
|
||||
self.assertTrue(
|
||||
Feature("test",standard=True,remove='x').include_by_default()
|
||||
)
|
||||
# Feature must have either kwargs, removes, or require_features
|
||||
self.assertRaises(DistutilsSetupError, Feature, "test")
|
||||
|
||||
def testAvailability(self):
|
||||
self.assertRaises(
|
||||
DistutilsPlatformError,
|
||||
self.dist.features['dwim'].include_in, self.dist
|
||||
)
|
||||
|
||||
def testFeatureOptions(self):
|
||||
dist = self.dist
|
||||
self.assertTrue(
|
||||
('with-dwim',None,'include DWIM') in dist.feature_options
|
||||
)
|
||||
self.assertTrue(
|
||||
('without-dwim',None,'exclude DWIM (default)') in dist.feature_options
|
||||
)
|
||||
self.assertTrue(
|
||||
('with-bar',None,'include bar (default)') in dist.feature_options
|
||||
)
|
||||
self.assertTrue(
|
||||
('without-bar',None,'exclude bar') in dist.feature_options
|
||||
)
|
||||
self.assertEqual(dist.feature_negopt['without-foo'],'with-foo')
|
||||
self.assertEqual(dist.feature_negopt['without-bar'],'with-bar')
|
||||
self.assertEqual(dist.feature_negopt['without-dwim'],'with-dwim')
|
||||
self.assertTrue(not 'without-baz' in dist.feature_negopt)
|
||||
|
||||
def testUseFeatures(self):
|
||||
dist = self.dist
|
||||
self.assertEqual(dist.with_foo,1)
|
||||
self.assertEqual(dist.with_bar,0)
|
||||
self.assertEqual(dist.with_baz,1)
|
||||
self.assertTrue(not 'bar_et' in dist.py_modules)
|
||||
self.assertTrue(not 'pkg.bar' in dist.packages)
|
||||
self.assertTrue('pkg.baz' in dist.packages)
|
||||
self.assertTrue('scripts/baz_it' in dist.scripts)
|
||||
self.assertTrue(('libfoo','foo/foofoo.c') in dist.libraries)
|
||||
self.assertEqual(dist.ext_modules,[])
|
||||
self.assertEqual(dist.require_features, [self.req])
|
||||
|
||||
# If we ask for bar, it should fail because we explicitly disabled
|
||||
# it on the command line
|
||||
self.assertRaises(DistutilsOptionError, dist.include_feature, 'bar')
|
||||
|
||||
def testFeatureWithInvalidRemove(self):
|
||||
self.assertRaises(
|
||||
SystemExit, makeSetup, features = {'x':Feature('x', remove='y')}
|
||||
)
|
||||
|
||||
class TestCommandTests(unittest.TestCase):
|
||||
|
||||
def testTestIsCommand(self):
|
||||
test_cmd = makeSetup().get_command_obj('test')
|
||||
self.assertTrue(isinstance(test_cmd, distutils.cmd.Command))
|
||||
|
||||
def testLongOptSuiteWNoDefault(self):
|
||||
ts1 = makeSetup(script_args=['test','--test-suite=foo.tests.suite'])
|
||||
ts1 = ts1.get_command_obj('test')
|
||||
ts1.ensure_finalized()
|
||||
self.assertEqual(ts1.test_suite, 'foo.tests.suite')
|
||||
|
||||
def testDefaultSuite(self):
|
||||
ts2 = makeSetup(test_suite='bar.tests.suite').get_command_obj('test')
|
||||
ts2.ensure_finalized()
|
||||
self.assertEqual(ts2.test_suite, 'bar.tests.suite')
|
||||
|
||||
def testDefaultWModuleOnCmdLine(self):
|
||||
ts3 = makeSetup(
|
||||
test_suite='bar.tests',
|
||||
script_args=['test','-m','foo.tests']
|
||||
).get_command_obj('test')
|
||||
ts3.ensure_finalized()
|
||||
self.assertEqual(ts3.test_module, 'foo.tests')
|
||||
self.assertEqual(ts3.test_suite, 'foo.tests.test_suite')
|
||||
|
||||
def testConflictingOptions(self):
|
||||
ts4 = makeSetup(
|
||||
script_args=['test','-m','bar.tests', '-s','foo.tests.suite']
|
||||
).get_command_obj('test')
|
||||
self.assertRaises(DistutilsOptionError, ts4.ensure_finalized)
|
||||
|
||||
def testNoSuite(self):
|
||||
ts5 = makeSetup().get_command_obj('test')
|
||||
ts5.ensure_finalized()
|
||||
self.assertEqual(ts5.test_suite, None)
|
||||
-2683
File diff suppressed because it is too large
Load Diff
-615
@@ -1,615 +0,0 @@
|
||||
10
|
||||
|
||||
dir
|
||||
89001
|
||||
http://svn.python.org/projects/sandbox/branches/setuptools-0.6
|
||||
http://svn.python.org/projects
|
||||
|
||||
|
||||
|
||||
2013-06-03T17:26:03.052972Z
|
||||
89000
|
||||
phillip.eby
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
6015fed2-1504-0410-9fe1-9d1591cc4771
|
||||
|
||||
api_tests.txt
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.948712Z
|
||||
dec366372ca14fbeaeb26f492bcf5725
|
||||
2013-05-15T22:04:59.389374Z
|
||||
88997
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
12312
|
||||
|
||||
setuptools.egg-info
|
||||
dir
|
||||
|
||||
README.txt
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.948712Z
|
||||
26f0dd5d095522ba3ad999b6b6777b92
|
||||
2011-05-31T20:10:56.416725Z
|
||||
88846
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
7615
|
||||
|
||||
easy_install.py
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.948712Z
|
||||
97b52fe7253bf4683f9f626f015eb72e
|
||||
2006-09-20T20:48:18.716070Z
|
||||
51935
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
126
|
||||
|
||||
setuptools
|
||||
dir
|
||||
|
||||
launcher.c
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.924700Z
|
||||
e5a8e77de9022688b80f77fc6d742fee
|
||||
2009-10-19T21:03:29.785400Z
|
||||
75544
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
7476
|
||||
|
||||
ez_setup.py
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.924700Z
|
||||
17e8ec5e08faccfcb08b5f8d5167ca14
|
||||
2011-01-20T18:50:00.815420Z
|
||||
88124
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
8350
|
||||
|
||||
version
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.924700Z
|
||||
e456da09e0c9e224a56302f8316b6dbf
|
||||
2007-01-09T19:21:05.921317Z
|
||||
53317
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1143
|
||||
|
||||
setup.py
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.924700Z
|
||||
d4e5b3c16bd61bfef6c0bb9377a3a3ea
|
||||
2013-05-15T22:04:59.389374Z
|
||||
88997
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
5228
|
||||
|
||||
release.sh
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.932704Z
|
||||
b1fd4054a1c107ff0f27baacd97be94c
|
||||
2009-10-28T17:12:45.227140Z
|
||||
75925
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1044
|
||||
|
||||
pkg_resources.txt
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.928702Z
|
||||
f497e7c92a4de207cbd9ab1943f93388
|
||||
2009-10-12T20:00:02.336146Z
|
||||
75385
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
94518
|
||||
|
||||
site.py
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.932704Z
|
||||
ebaac6fb6525f77ca950d22e6f8315df
|
||||
2006-03-11T00:39:09.666740Z
|
||||
42965
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2362
|
||||
|
||||
version.dat
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.932704Z
|
||||
8e14ecea32b9874cd7d29277494554c0
|
||||
2009-10-28T17:12:45.227140Z
|
||||
75925
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
80
|
||||
|
||||
virtual-python.py
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.932704Z
|
||||
aa857add3b5563238f0a904187f5ded9
|
||||
2005-10-17T02:26:39.000000Z
|
||||
41262
|
||||
pje
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3898
|
||||
|
||||
setup.cfg
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.932704Z
|
||||
eda883e744fce83f8107ad8dc8303536
|
||||
2006-09-21T22:26:48.050256Z
|
||||
51965
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
296
|
||||
|
||||
setuptools.txt
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.940708Z
|
||||
11926256f06046b196eaf814772504e7
|
||||
2013-05-15T22:04:59.389374Z
|
||||
88997
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
149832
|
||||
|
||||
pkg_resources.py
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.940708Z
|
||||
b63a30f5f0f0225a788c2c0e3430b3cf
|
||||
2013-05-15T22:04:59.389374Z
|
||||
88997
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
90397
|
||||
|
||||
tests
|
||||
dir
|
||||
|
||||
wikiup.cfg
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.944710Z
|
||||
34ad845a5e0a0b46458557fa910bf429
|
||||
2008-08-21T17:23:50.797633Z
|
||||
65935
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
136
|
||||
|
||||
EasyInstall.txt
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2013-06-19T13:20:47.944710Z
|
||||
e97387c517f70fc18a377e42d19d64d4
|
||||
2013-05-15T22:04:59.389374Z
|
||||
88997
|
||||
phillip.eby
|
||||
has-props
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
82495
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
import os
|
||||
import zipfile
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import shutil
|
||||
import stat
|
||||
import unicodedata
|
||||
|
||||
from subprocess import Popen as _Popen, PIPE as _PIPE
|
||||
|
||||
|
||||
def _extract(self, member, path=None, pwd=None):
|
||||
"""for zipfile py2.5 borrowed from cpython"""
|
||||
if not isinstance(member, zipfile.ZipInfo):
|
||||
member = self.getinfo(member)
|
||||
|
||||
if path is None:
|
||||
path = os.getcwd()
|
||||
|
||||
return _extract_member(self, member, path, pwd)
|
||||
|
||||
|
||||
def _extract_from_zip(self, name, dest_path):
|
||||
dest_file = open(dest_path, 'wb')
|
||||
try:
|
||||
dest_file.write(self.read(name))
|
||||
finally:
|
||||
dest_file.close()
|
||||
|
||||
|
||||
def _extract_member(self, member, targetpath, pwd):
|
||||
"""for zipfile py2.5 borrowed from cpython"""
|
||||
# build the destination pathname, replacing
|
||||
# forward slashes to platform specific separators.
|
||||
# Strip trailing path separator, unless it represents the root.
|
||||
if (targetpath[-1:] in (os.path.sep, os.path.altsep)
|
||||
and len(os.path.splitdrive(targetpath)[1]) > 1):
|
||||
targetpath = targetpath[:-1]
|
||||
|
||||
# don't include leading "/" from file name if present
|
||||
if member.filename[0] == '/':
|
||||
targetpath = os.path.join(targetpath, member.filename[1:])
|
||||
else:
|
||||
targetpath = os.path.join(targetpath, member.filename)
|
||||
|
||||
targetpath = os.path.normpath(targetpath)
|
||||
|
||||
# Create all upper directories if necessary.
|
||||
upperdirs = os.path.dirname(targetpath)
|
||||
if upperdirs and not os.path.exists(upperdirs):
|
||||
os.makedirs(upperdirs)
|
||||
|
||||
if member.filename[-1] == '/':
|
||||
if not os.path.isdir(targetpath):
|
||||
os.mkdir(targetpath)
|
||||
return targetpath
|
||||
|
||||
_extract_from_zip(self, member.filename, targetpath)
|
||||
|
||||
return targetpath
|
||||
|
||||
|
||||
def _remove_dir(target):
|
||||
|
||||
#on windows this seems to a problem
|
||||
for dir_path, dirs, files in os.walk(target):
|
||||
os.chmod(dir_path, stat.S_IWRITE)
|
||||
for filename in files:
|
||||
os.chmod(os.path.join(dir_path, filename), stat.S_IWRITE)
|
||||
shutil.rmtree(target)
|
||||
|
||||
|
||||
class ZippedEnvironment(unittest.TestCase):
|
||||
|
||||
datafile = None
|
||||
dataname = None
|
||||
old_cwd = None
|
||||
|
||||
def setUp(self):
|
||||
if self.datafile is None or self.dataname is None:
|
||||
return
|
||||
|
||||
if not os.path.isfile(self.datafile):
|
||||
self.old_cwd = None
|
||||
return
|
||||
|
||||
self.old_cwd = os.getcwd()
|
||||
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
zip_file, source, target = [None, None, None]
|
||||
try:
|
||||
zip_file = zipfile.ZipFile(self.datafile)
|
||||
for files in zip_file.namelist():
|
||||
_extract(zip_file, files, self.temp_dir)
|
||||
finally:
|
||||
if zip_file:
|
||||
zip_file.close()
|
||||
del zip_file
|
||||
|
||||
os.chdir(os.path.join(self.temp_dir, self.dataname))
|
||||
|
||||
def tearDown(self):
|
||||
#Assume setUp was never completed
|
||||
if self.dataname is None or self.datafile is None:
|
||||
return
|
||||
|
||||
try:
|
||||
if self.old_cwd:
|
||||
os.chdir(self.old_cwd)
|
||||
_remove_dir(self.temp_dir)
|
||||
except OSError:
|
||||
#sigh?
|
||||
pass
|
||||
|
||||
|
||||
def _which_dirs(cmd):
|
||||
result = set()
|
||||
for path in os.environ.get('PATH', '').split(os.pathsep):
|
||||
filename = os.path.join(path, cmd)
|
||||
if os.access(filename, os.X_OK):
|
||||
result.add(path)
|
||||
return result
|
||||
|
||||
|
||||
def run_setup_py(cmd, pypath=None, path=None,
|
||||
data_stream=0, env=None):
|
||||
"""
|
||||
Execution command for tests, separate from those used by the
|
||||
code directly to prevent accidental behavior issues
|
||||
"""
|
||||
if env is None:
|
||||
env = dict()
|
||||
for envname in os.environ:
|
||||
env[envname] = os.environ[envname]
|
||||
|
||||
#override the python path if needed
|
||||
if pypath is not None:
|
||||
env["PYTHONPATH"] = pypath
|
||||
|
||||
#overide the execution path if needed
|
||||
if path is not None:
|
||||
env["PATH"] = path
|
||||
if not env.get("PATH", ""):
|
||||
env["PATH"] = _which_dirs("tar").union(_which_dirs("gzip"))
|
||||
env["PATH"] = os.pathsep.join(env["PATH"])
|
||||
|
||||
cmd = [sys.executable, "setup.py"] + list(cmd)
|
||||
|
||||
#regarding the shell argument, see: http://bugs.python.org/issue8557
|
||||
try:
|
||||
proc = _Popen(cmd, stdout=_PIPE, stderr=_PIPE,
|
||||
shell=(sys.platform == 'win32'), env=env)
|
||||
|
||||
data = proc.communicate()[data_stream]
|
||||
except OSError:
|
||||
return 1, ''
|
||||
|
||||
#decode the console string if needed
|
||||
if hasattr(data, "decode"):
|
||||
data = data.decode() # should use the preffered encoding
|
||||
data = unicodedata.normalize('NFC', data)
|
||||
|
||||
#communciate calls wait()
|
||||
return proc.returncode, data
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
<html><body>
|
||||
<a href="/foobar-0.1.tar.gz#md5=1__bad_md5___">bad old link</a>
|
||||
</body></html>
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
<html><body>
|
||||
<a href="/foobar-0.1.tar.gz#md5=0_correct_md5">foobar-0.1.tar.gz</a><br/>
|
||||
<a href="../../external.html" rel="homepage">external homepage</a><br/>
|
||||
</body></html>
|
||||
@@ -1,14 +0,0 @@
|
||||
import unittest
|
||||
|
||||
try:
|
||||
# provide skipIf for Python 2.4-2.6
|
||||
skipIf = unittest.skipIf
|
||||
except AttributeError:
|
||||
def skipIf(condition, reason):
|
||||
def skipper(func):
|
||||
def skip(*args, **kwargs):
|
||||
return
|
||||
if condition:
|
||||
return skip
|
||||
return func
|
||||
return skipper
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
result = 'passed'
|
||||
@@ -1,82 +0,0 @@
|
||||
"""Basic http server for tests to simulate PyPI or custom indexes
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
from setuptools.compat import BaseHTTPRequestHandler
|
||||
from setuptools.compat import (urllib2, URLError, HTTPServer,
|
||||
SimpleHTTPRequestHandler)
|
||||
|
||||
class IndexServer(HTTPServer):
|
||||
"""Basic single-threaded http server simulating a package index
|
||||
|
||||
You can use this server in unittest like this::
|
||||
s = IndexServer()
|
||||
s.start()
|
||||
index_url = s.base_url() + 'mytestindex'
|
||||
# do some test requests to the index
|
||||
# The index files should be located in setuptools/tests/indexes
|
||||
s.stop()
|
||||
"""
|
||||
def __init__(self, server_address=('', 0),
|
||||
RequestHandlerClass=SimpleHTTPRequestHandler):
|
||||
HTTPServer.__init__(self, server_address, RequestHandlerClass)
|
||||
self._run = True
|
||||
|
||||
def serve(self):
|
||||
while self._run:
|
||||
self.handle_request()
|
||||
|
||||
def start(self):
|
||||
self.thread = threading.Thread(target=self.serve)
|
||||
self.thread.start()
|
||||
|
||||
def stop(self):
|
||||
"Stop the server"
|
||||
|
||||
# Let the server finish the last request and wait for a new one.
|
||||
time.sleep(0.1)
|
||||
|
||||
# self.shutdown is not supported on python < 2.6, so just
|
||||
# set _run to false, and make a request, causing it to
|
||||
# terminate.
|
||||
self._run = False
|
||||
url = 'http://127.0.0.1:%(server_port)s/' % vars(self)
|
||||
try:
|
||||
if sys.version_info >= (2, 6):
|
||||
urllib2.urlopen(url, timeout=5)
|
||||
else:
|
||||
urllib2.urlopen(url)
|
||||
except URLError:
|
||||
# ignore any errors; all that's important is the request
|
||||
pass
|
||||
self.thread.join()
|
||||
self.socket.close()
|
||||
|
||||
def base_url(self):
|
||||
port = self.server_port
|
||||
return 'http://127.0.0.1:%s/setuptools/tests/indexes/' % port
|
||||
|
||||
class RequestRecorder(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
requests = vars(self.server).setdefault('requests', [])
|
||||
requests.append(self)
|
||||
self.send_response(200, 'OK')
|
||||
|
||||
class MockServer(HTTPServer, threading.Thread):
|
||||
"""
|
||||
A simple HTTP Server that records the requests made to it.
|
||||
"""
|
||||
def __init__(self, server_address=('', 0),
|
||||
RequestHandlerClass=RequestRecorder):
|
||||
HTTPServer.__init__(self, server_address, RequestHandlerClass)
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.requests = []
|
||||
|
||||
def run(self):
|
||||
self.serve_forever()
|
||||
|
||||
def url(self):
|
||||
return 'http://localhost:%(server_port)s/' % vars(self)
|
||||
url = property(url)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user