New distutils from Python 2.3a2

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_4_BRANCH@19280 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2003-02-21 21:45:58 +00:00
parent ab60f861e4
commit ced9b1048c
10 changed files with 353 additions and 15 deletions

View File

@@ -7,6 +7,7 @@ Implements the Distutils 'build_scripts' command."""
__revision__ = "$Id$" __revision__ = "$Id$"
import sys, os, re import sys, os, re
from stat import ST_MODE
from distutils import sysconfig from distutils import sysconfig
from distutils.core import Command from distutils.core import Command
from distutils.dep_util import newer from distutils.dep_util import newer
@@ -54,10 +55,12 @@ class build_scripts (Command):
line to refer to the current Python interpreter as we copy. line to refer to the current Python interpreter as we copy.
""" """
self.mkpath(self.build_dir) self.mkpath(self.build_dir)
outfiles = []
for script in self.scripts: for script in self.scripts:
adjust = 0 adjust = 0
script = convert_path(script) script = convert_path(script)
outfile = os.path.join(self.build_dir, os.path.basename(script)) outfile = os.path.join(self.build_dir, os.path.basename(script))
outfiles.append(outfile)
if not self.force and not newer(script, outfile): if not self.force and not newer(script, outfile):
log.debug("not copying %s (up-to-date)", script) log.debug("not copying %s (up-to-date)", script)
@@ -106,6 +109,18 @@ class build_scripts (Command):
f.close() f.close()
self.copy_file(script, outfile) self.copy_file(script, outfile)
if os.name == 'posix':
for file in outfiles:
if self.dry_run:
log.info("changing mode of %s", file)
else:
oldmode = os.stat(file)[ST_MODE] & 07777
newmode = (oldmode | 0555) & 07777
if newmode != oldmode:
log.info("changing mode of %s from %o to %o",
file, oldmode, newmode)
os.chmod(file, newmode)
# copy_scripts () # copy_scripts ()
# class build_scripts # class build_scripts

View File

@@ -17,6 +17,7 @@ import sys, os, string, re
from types import * from types import *
from distutils.core import Command from distutils.core import Command
from distutils.errors import DistutilsExecError from distutils.errors import DistutilsExecError
from distutils.sysconfig import customize_compiler
from distutils import log from distutils import log
LANG_EXT = {'c': '.c', LANG_EXT = {'c': '.c',
@@ -104,6 +105,7 @@ class config (Command):
if not isinstance(self.compiler, CCompiler): if not isinstance(self.compiler, CCompiler):
self.compiler = new_compiler(compiler=self.compiler, self.compiler = new_compiler(compiler=self.compiler,
dry_run=self.dry_run, force=1) dry_run=self.dry_run, force=1)
customize_compiler(self.compiler)
if self.include_dirs: if self.include_dirs:
self.compiler.set_include_dirs(self.include_dirs) self.compiler.set_include_dirs(self.include_dirs)
if self.libraries: if self.libraries:
@@ -151,7 +153,8 @@ class config (Command):
library_dirs=library_dirs, library_dirs=library_dirs,
target_lang=lang) target_lang=lang)
prog = prog + self.compiler.exe_extension if self.compiler.exe_extension is not None:
prog = prog + self.compiler.exe_extension
self.temp_files.append(prog) self.temp_files.append(prog)
return (src, obj, prog) return (src, obj, prog)

View File

@@ -0,0 +1,292 @@
"""distutils.command.register
Implements the Distutils 'register' command (register with the repository).
"""
# created 2002/10/21, Richard Jones
__revision__ = "$Id$"
import sys, os, string, urllib2, getpass, urlparse
import StringIO, ConfigParser
from distutils.core import Command
from distutils.errors import *
class register(Command):
description = "register the distribution with the repository"
DEFAULT_REPOSITORY = 'http://www.python.org/pypi'
user_options = [
('repository=', 'r',
"url of repository [default: %s]"%DEFAULT_REPOSITORY),
('verify', None,
'verify the package metadata for correctness'),
('list-classifiers', None,
'list the valid Trove classifiers'),
('show-response', None,
'display full response text from server'),
]
boolean_options = ['verify', 'show-response', 'list-classifiers']
def initialize_options(self):
self.repository = None
self.verify = 0
self.show_response = 0
self.list_classifiers = 0
def finalize_options(self):
if self.repository is None:
self.repository = self.DEFAULT_REPOSITORY
def run(self):
self.check_metadata()
if self.verify:
self.verify_metadata()
elif self.list_classifiers:
self.classifiers()
else:
self.send_metadata()
def check_metadata(self):
"""Ensure that all required elements of meta-data (name, version,
URL, (author and author_email) or (maintainer and
maintainer_email)) are supplied by the Distribution object; warn if
any are missing.
"""
metadata = self.distribution.metadata
missing = []
for attr in ('name', 'version', 'url'):
if not (hasattr(metadata, attr) and getattr(metadata, attr)):
missing.append(attr)
if missing:
self.warn("missing required meta-data: " +
string.join(missing, ", "))
if metadata.author:
if not metadata.author_email:
self.warn("missing meta-data: if 'author' supplied, " +
"'author_email' must be supplied too")
elif metadata.maintainer:
if not metadata.maintainer_email:
self.warn("missing meta-data: if 'maintainer' supplied, " +
"'maintainer_email' must be supplied too")
else:
self.warn("missing meta-data: either (author and author_email) " +
"or (maintainer and maintainer_email) " +
"must be supplied")
def classifiers(self):
''' Fetch the list of classifiers from the server.
'''
response = urllib2.urlopen(self.repository+'?:action=list_classifiers')
print response.read()
def verify_metadata(self):
''' Send the metadata to the package index server to be checked.
'''
# send the info to the server and report the result
(code, result) = self.post_to_server(self.build_post_data('verify'))
print 'Server response (%s): %s'%(code, result)
def send_metadata(self):
''' Send the metadata to the package index server.
Well, do the following:
1. figure who the user is, and then
2. send the data as a Basic auth'ed POST.
First we try to read the username/password from $HOME/.pypirc,
which is a ConfigParser-formatted file with a section
[server-login] containing username and password entries (both
in clear text). Eg:
[server-login]
username: fred
password: sekrit
Otherwise, to figure who the user is, we offer the user three
choices:
1. use existing login,
2. register as a new user, or
3. set the password to a random string and email the user.
'''
choice = 'x'
username = password = ''
# see if we can short-cut and get the username/password from the
# config
config = None
if os.environ.has_key('HOME'):
rc = os.path.join(os.environ['HOME'], '.pypirc')
if os.path.exists(rc):
print 'Using PyPI login from %s'%rc
config = ConfigParser.ConfigParser()
config.read(rc)
username = config.get('server-login', 'username')
password = config.get('server-login', 'password')
choice = '1'
# get the user's login info
choices = '1 2 3 4'.split()
while choice not in choices:
print '''We need to know who you are, so please choose either:
1. use your existing login,
2. register as a new user,
3. have the server generate a new password for you (and email it to you), or
4. quit
Your selection [default 1]: ''',
choice = raw_input()
if not choice:
choice = '1'
elif choice not in choices:
print 'Please choose one of the four options!'
if choice == '1':
# get the username and password
while not username:
username = raw_input('Username: ')
while not password:
password = getpass.getpass('Password: ')
# set up the authentication
auth = urllib2.HTTPPasswordMgr()
host = urlparse.urlparse(self.repository)[1]
auth.add_password('pypi', host, username, password)
# send the info to the server and report the result
code, result = self.post_to_server(self.build_post_data('submit'),
auth)
print 'Server response (%s): %s'%(code, result)
# possibly save the login
if os.environ.has_key('HOME') and config is None and code == 200:
rc = os.path.join(os.environ['HOME'], '.pypirc')
print 'I can store your PyPI login so future submissions will be faster.'
print '(the login will be stored in %s)'%rc
choice = 'X'
while choice.lower() not in 'yn':
choice = raw_input('Save your login (y/N)?')
if not choice:
choice = 'n'
if choice.lower() == 'y':
f = open(rc, 'w')
f.write('[server-login]\nusername:%s\npassword:%s\n'%(
username, password))
f.close()
try:
os.chmod(rc, 0600)
except:
pass
elif choice == '2':
data = {':action': 'user'}
data['name'] = data['password'] = data['email'] = ''
data['confirm'] = None
while not data['name']:
data['name'] = raw_input('Username: ')
while data['password'] != data['confirm']:
while not data['password']:
data['password'] = getpass.getpass('Password: ')
while not data['confirm']:
data['confirm'] = getpass.getpass(' Confirm: ')
if data['password'] != data['confirm']:
data['password'] = ''
data['confirm'] = None
print "Password and confirm don't match!"
while not data['email']:
data['email'] = raw_input(' EMail: ')
code, result = self.post_to_server(data)
if code != 200:
print 'Server response (%s): %s'%(code, result)
else:
print 'You will receive an email shortly.'
print 'Follow the instructions in it to complete registration.'
elif choice == '3':
data = {':action': 'password_reset'}
data['email'] = ''
while not data['email']:
data['email'] = raw_input('Your email address: ')
code, result = self.post_to_server(data)
print 'Server response (%s): %s'%(code, result)
def build_post_data(self, action):
# figure the data to send - the metadata plus some additional
# information used by the package server
meta = self.distribution.metadata
data = {
':action': action,
'metadata_version' : '1.0',
'name': meta.get_name(),
'version': meta.get_version(),
'summary': meta.get_description(),
'home_page': meta.get_url(),
'author': meta.get_contact(),
'author_email': meta.get_contact_email(),
'license': meta.get_licence(),
'description': meta.get_long_description(),
'keywords': meta.get_keywords(),
'platform': meta.get_platforms(),
'classifiers': meta.get_classifiers(),
'download_url': meta.get_download_url(),
}
return data
def post_to_server(self, data, auth=None):
''' Post a query to the server, and return a string response.
'''
# Build up the MIME payload for the urllib2 POST data
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = '\n--' + boundary
end_boundary = sep_boundary + '--'
body = StringIO.StringIO()
for key, value in data.items():
# handle multiple entries for the same name
if type(value) != type([]):
value = [value]
for value in value:
value = str(value)
body.write(sep_boundary)
body.write('\nContent-Disposition: form-data; name="%s"'%key)
body.write("\n\n")
body.write(value)
if value and value[-1] == '\r':
body.write('\n') # write an extra newline (lurve Macs)
body.write(end_boundary)
body.write("\n")
body = body.getvalue()
# build the Request
headers = {
'Content-type': 'multipart/form-data; boundary=%s'%boundary,
'Content-length': str(len(body))
}
req = urllib2.Request(self.repository, body, headers)
# handle HTTP and include the Basic Auth handler
opener = urllib2.build_opener(
urllib2.HTTPBasicAuthHandler(password_mgr=auth)
)
data = ''
try:
result = opener.open(req)
except urllib2.HTTPError, e:
if self.show_response:
data = e.fp.read()
result = e.code, e.msg
except urllib2.URLError, e:
result = 500, str(e)
else:
if self.show_response:
data = result.read()
result = 200, 'OK'
if self.show_response:
print '-'*75, data, '-'*75
return result

View File

@@ -42,6 +42,19 @@ def gen_usage (script_name):
_setup_stop_after = None _setup_stop_after = None
_setup_distribution = None _setup_distribution = None
# Legal keyword arguments for the setup() function
setup_keywords = ('distclass', 'script_name', 'script_args', 'options',
'name', 'version', 'author', 'author_email',
'maintainer', 'maintainer_email', 'url', 'license',
'description', 'long_description', 'keywords',
'platforms', 'classifiers', 'download_url')
# Legal keyword arguments for the Extension constructor
extension_keywords = ('name', 'sources', 'include_dirs',
'define_macros', 'undef_macros',
'library_dirs', 'libraries', 'runtime_library_dirs',
'extra_objects', 'extra_compile_args', 'extra_link_args',
'export_symbols', 'depends', 'language')
def setup (**attrs): def setup (**attrs):
"""The gateway to the Distutils: do everything your setup script needs """The gateway to the Distutils: do everything your setup script needs
@@ -226,11 +239,3 @@ def run_setup (script_name, script_args=None, stop_after="run"):
# run_setup () # run_setup ()
def get_distutil_options ():
"""Returns a list of strings recording changes to the Distutils.
setup.py files can then do:
if 'optional-thing' in get_distutil_options():
...
"""
return []

View File

@@ -93,6 +93,8 @@ class Distribution:
"print the long package description"), "print the long package description"),
('platforms', None, ('platforms', None,
"print the list of platforms"), "print the list of platforms"),
('classifiers', None,
"print the list of classifiers"),
('keywords', None, ('keywords', None,
"print the list of keywords"), "print the list of keywords"),
] ]
@@ -634,6 +636,8 @@ class Distribution:
value = getattr(self.metadata, "get_"+opt)() value = getattr(self.metadata, "get_"+opt)()
if opt in ['keywords', 'platforms']: if opt in ['keywords', 'platforms']:
print string.join(value, ',') print string.join(value, ',')
elif opt == 'classifiers':
print string.join(value, '\n')
else: else:
print value print value
any_display_options = 1 any_display_options = 1
@@ -962,7 +966,8 @@ class DistributionMetadata:
"maintainer", "maintainer_email", "url", "maintainer", "maintainer_email", "url",
"license", "description", "long_description", "license", "description", "long_description",
"keywords", "platforms", "fullname", "contact", "keywords", "platforms", "fullname", "contact",
"contact_email", "licence") "contact_email", "licence", "classifiers",
"download_url")
def __init__ (self): def __init__ (self):
self.name = None self.name = None
@@ -977,6 +982,8 @@ class DistributionMetadata:
self.long_description = None self.long_description = None
self.keywords = None self.keywords = None
self.platforms = None self.platforms = None
self.classifiers = None
self.download_url = None
def write_pkg_info (self, base_dir): def write_pkg_info (self, base_dir):
"""Write the PKG-INFO file into the release tree. """Write the PKG-INFO file into the release tree.
@@ -992,6 +999,8 @@ class DistributionMetadata:
pkg_info.write('Author: %s\n' % self.get_contact() ) pkg_info.write('Author: %s\n' % self.get_contact() )
pkg_info.write('Author-email: %s\n' % self.get_contact_email() ) pkg_info.write('Author-email: %s\n' % self.get_contact_email() )
pkg_info.write('License: %s\n' % self.get_license() ) pkg_info.write('License: %s\n' % self.get_license() )
if self.download_url:
pkg_info.write('Download-URL: %s\n' % self.download_url)
long_desc = rfc822_escape( self.get_long_description() ) long_desc = rfc822_escape( self.get_long_description() )
pkg_info.write('Description: %s\n' % long_desc) pkg_info.write('Description: %s\n' % long_desc)
@@ -1003,6 +1012,9 @@ class DistributionMetadata:
for platform in self.get_platforms(): for platform in self.get_platforms():
pkg_info.write('Platform: %s\n' % platform ) pkg_info.write('Platform: %s\n' % platform )
for classifier in self.get_classifiers():
pkg_info.write('Classifier: %s\n' % classifier )
pkg_info.close() pkg_info.close()
# write_pkg_info () # write_pkg_info ()
@@ -1059,6 +1071,12 @@ class DistributionMetadata:
def get_platforms(self): def get_platforms(self):
return self.platforms or ["UNKNOWN"] return self.platforms or ["UNKNOWN"]
def get_classifiers(self):
return self.classifiers or []
def get_download_url(self):
return self.download_url or "UNKNOWN"
# class DistributionMetadata # class DistributionMetadata

View File

@@ -82,6 +82,8 @@ class Extension:
from the source extensions if not provided. from the source extensions if not provided.
""" """
# When adding arguments to this constructor, be sure to update
# setup_keywords in core.py.
def __init__ (self, name, sources, def __init__ (self, name, sources,
include_dirs=None, include_dirs=None,
define_macros=None, define_macros=None,

View File

@@ -310,7 +310,7 @@ class MSVCCompiler (CCompiler) :
input_opt = src input_opt = src
output_opt = "/fo" + obj output_opt = "/fo" + obj
try: try:
self.spawn ([self.rc] + pp_opts + # Robin added pp_opts self.spawn ([self.rc] + pp_opts +
[output_opt] + [input_opt]) [output_opt] + [input_opt])
except DistutilsExecError, msg: except DistutilsExecError, msg:
raise CompileError, msg raise CompileError, msg

View File

@@ -146,8 +146,8 @@ def customize_compiler(compiler):
varies across Unices and is stored in Python's Makefile. varies across Unices and is stored in Python's Makefile.
""" """
if compiler.compiler_type == "unix": if compiler.compiler_type == "unix":
(cc, cxx, opt, ccshared, ldshared, so_ext) = \ (cc, cxx, opt, basecflags, ccshared, ldshared, so_ext) = \
get_config_vars('CC', 'CXX', 'OPT', 'CCSHARED', 'LDSHARED', 'SO') get_config_vars('CC', 'CXX', 'OPT', 'BASECFLAGS', 'CCSHARED', 'LDSHARED', 'SO')
if os.environ.has_key('CC'): if os.environ.has_key('CC'):
cc = os.environ['CC'] cc = os.environ['CC']
@@ -159,6 +159,8 @@ def customize_compiler(compiler):
cpp = cc + " -E" # not always cpp = cc + " -E" # not always
if os.environ.has_key('LDFLAGS'): if os.environ.has_key('LDFLAGS'):
ldshared = ldshared + ' ' + os.environ['LDFLAGS'] ldshared = ldshared + ' ' + os.environ['LDFLAGS']
if basecflags:
opt = basecflags + ' ' + opt
if os.environ.has_key('CFLAGS'): if os.environ.has_key('CFLAGS'):
opt = opt + ' ' + os.environ['CFLAGS'] opt = opt + ' ' + os.environ['CFLAGS']
ldshared = ldshared + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS']

View File

@@ -40,10 +40,11 @@ def get_platform ():
(osname, host, release, version, machine) = os.uname() (osname, host, release, version, machine) = os.uname()
# Convert the OS name to lowercase and remove '/' characters # Convert the OS name to lowercase, remove '/' characters
# (to accommodate BSD/OS) # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh")
osname = string.lower(osname) osname = string.lower(osname)
osname = string.replace(osname, '/', '') osname = string.replace(osname, '/', '')
machine = string.replace(machine, ' ', '_')
if osname[:5] == "linux": if osname[:5] == "linux":
# At least on Linux/Intel, 'machine' is the processor -- # At least on Linux/Intel, 'machine' is the processor --