diff --git a/python/which/LICENSE.txt b/python/which/LICENSE.txt new file mode 100644 index 0000000000000..de85cd53a856e --- /dev/null +++ b/python/which/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2002-2005 ActiveState Corp. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/python/which/MANIFEST.in b/python/which/MANIFEST.in new file mode 100644 index 0000000000000..3b8970ab829c6 --- /dev/null +++ b/python/which/MANIFEST.in @@ -0,0 +1,3 @@ +include *.py *.cpp *.in which.exe Makefile* *.txt logo.jpg +exclude *~ +recursive-include test *.txt *.py diff --git a/python/which/Makefile.win b/python/which/Makefile.win new file mode 100644 index 0000000000000..c400aafd4b855 --- /dev/null +++ b/python/which/Makefile.win @@ -0,0 +1,21 @@ +# Copyright (c) 2002-2003 ActiveState Corp. +# Author: Trent Mick (TrentM@ActiveState.com) +# +# A Makefile to do this: launcher.cpp -> foo.exe + +APPNAME=which + +# for release: +CFLAGS=-D_CONSOLE -D_MBCS -DWIN32 -W3 -Ox -DNDEBUG -D_NDEBUG -MD +LDFLAGS=/subsystem:console kernel32.lib user32.lib gdi32.lib advapi32.lib shlwapi.lib +# for debug: +# CFLAGS = -D_CONSOLE -D_MBCS /DWIN32 /Zi /Od /DDEBUG /D_DEBUG /MDd +# LDFLAGS += /DEBUG + +$(APPNAME).exe: launcher.cpp + cl -nologo $(CFLAGS) -c launcher.cpp + link -nologo $(LDFLAGS) launcher.obj -out:$(APPNAME).exe + +clean: + if exist launcher.obj; del launcher.obj + if exist $(APPNAME).exe; del $(APPNAME).exe diff --git a/python/which/PKG-INFO b/python/which/PKG-INFO new file mode 100644 index 0000000000000..bfa8baa280728 --- /dev/null +++ b/python/which/PKG-INFO @@ -0,0 +1,21 @@ +Metadata-Version: 1.0 +Name: which +Version: 1.1.0 +Summary: a portable GNU which replacement +Home-page: http://trentm.com/projects/which/ +Author: Trent Mick +Author-email: TrentM@ActiveState.com +License: MIT License +Description: This is a GNU which replacement with the following features: + - it is portable (Windows, Linux); + - it understands PATHEXT on Windows; + - it can print all matches on the PATH; + - it can note "near misses" on the PATH (e.g. files that match but + may not, say, have execute permissions; and + - it can be used as a Python module. + +Keywords: which,find,path,where +Platform: Windows +Platform: Linux +Platform: Mac OS X +Platform: Unix diff --git a/python/which/README.txt b/python/which/README.txt new file mode 100644 index 0000000000000..6ece7f6de2737 --- /dev/null +++ b/python/which/README.txt @@ -0,0 +1,229 @@ +which.py -- a portable GNU which replacement +============================================ + +Download the latest which.py packages from here: + (source) http://trentm.com/downloads/which/1.1.0/which-1.1.0.zip + + +Home : http://trentm.com/projects/which/ +License : MIT (see LICENSE.txt) +Platforms : Windows, Linux, Mac OS X, Unix +Current Version : 1.1 +Dev Status : mature, has been heavily used in a commercial product for + over 2 years +Requirements : Python >= 2.3 (http://www.activestate.com/ActivePython/) + + +What's new? +----------- + +I have moved hosting of `which.py` from my old [Starship +pages](http://starship.python.net/~tmick/) to this site. These starter +docs have been improved a little bit. See the [Change Log](#changelog) +below for more. + +**WARNING**: If you are upgrading your `which.py` and you also use my +[process.py](../process/) module, you must upgrade `process.py` as well +because of the `_version_/__version__` change in v1.1.0. + + +Why which.py? +------------- + +`which.py` is a small GNU-which replacement. It has the following +features: + +- it is portable (Windows, Linux, Mac OS X, Un*x); +- it understands PATHEXT and "App Paths" registration on Windows + (i.e. it will find everything that `start` does from the command shell); +- it can print all matches on the PATH; +- it can note "near misses" on the PATH (e.g. files that match but may + not, say, have execute permissions); and +- it can be used as a Python module. + +I also would be happy to have this be a replacement for the `which.py` in the +Python CVS tree at `dist/src/Tools/scripts/which.py` which is +Unix-specific and not usable as a module; and perhaps for inclusion in +the stdlib. + +Please send any feedback to [Trent Mick](mailto:TrentM@ActiveState.com). + + +Install Notes +------------- + +Download the latest `which.py` source package, unzip it, and run +`python setup.py install`: + + unzip which-1.1.0.zip + cd which-1.1.0 + python setup.py install + +If your install fails then please visit [the Troubleshooting +FAQ](http://trentm.com/faq.html#troubleshooting-python-package-installation). + +`which.py` can be used both as a module and as a script. By default, +`which.py` will be installed into your Python's `site-packages` +directory so it can be used as a module. On *Windows only*, `which.py` +(and the launcher stub `which.exe`) will be installed in the Python +install dir to (hopefully) put `which` on your PATH. + +On Un*x platforms (including Linux and Mac OS X) there is often a +`which` executable already on your PATH. To use this `which` instead of +your system's on those platforms you can manually do one of the +following: + +- Copy `which.py` to `which` somewhere on your PATH ahead of the system + `which`. This can be a symlink, as well: + + ln -s /PATH/TO/site-packages/which.py /usr/local/bin/which + +- Python 2.4 users might want to use Python's new '-m' switch and setup + and alias: + + alias which='python -m which' + + or stub script like this: + + #!/bin/sh + python -m which $@ + + +Getting Started +--------------- + +Currently the best intro to using `which.py` as a module is its module +documentation. Either install `which.py` and run: + + pydoc which + +take a look at `which.py` in your editor or [here](which.py), or read +on. Most commonly you'll use the `which()` method to find an +executable: + + >>> import which + >>> which.which("perl") + '/usr/local/bin/perl' + +Or you might want to know if you have multiple versions on your path: + + >>> which.whichall("perl") + ['/usr/local/bin/perl', '/usr/bin/perl'] + +Use `verbose` to see where your executable is being found. (On Windows +this might not always be so obvious as your PATH environment variable. +There is an "App Paths" area of the registry where the `start` command +will find "registered" executables -- `which.py` mimics this.) + + >>> which.whichall("perl", verbose=True) + [('/usr/local/bin/perl', 'from PATH element 10'), + ('/usr/bin/perl', 'from PATH element 15')] + +You can restrict the searched path: + + >>> which.whichall("perl", path=["/usr/bin"]) + ['/usr/bin/perl'] + +There is a generator interface: + + >>> for perl in which.whichgen("perl"): + ... print "found a perl here:", perl + ... + found a perl here: /usr/local/bin/perl + found a perl here: /usr/bin/perl + +An exception is raised if your executable is not found: + + >>> which.which("fuzzywuzzy") + Traceback (most recent call last): + ... + which.WhichError: Could not find 'fuzzywuzzy' on the path. + >>> + +There are some other options too: + + >>> help(which.which) + ... + +Run `which --help` to see command-line usage: + + $ which --help + Show the full path of commands. + + Usage: + which [...] [...] + + Options: + -h, --help Print this help and exit. + -V, --version Print the version info and exit. + + -a, --all Print *all* matching paths. + -v, --verbose Print out how matches were located and + show near misses on stderr. + -q, --quiet Just print out matches. I.e., do not print out + near misses. + + -p , --path= + An alternative path (list of directories) may + be specified for searching. + -e , --exts= + Specify a list of extensions to consider instead + of the usual list (';'-separate list, Windows + only). + + Show the full path to the program that would be run for each given + command name, if any. Which, like GNU's which, returns the number of + failed arguments, or -1 when no was given. + + Near misses include duplicates, non-regular files and (on Un*x) + files without executable access. + + +Change Log +---------- + +### v1.1.0 +- Change version attributes and semantics. Before: had a _version_ + tuple. After: __version__ is a string, __version_info__ is a tuple. + +### v1.0.3 +- Move hosting of which.py to trentm.com. Tweaks to associated bits + (README.txt, etc.) + +### v1.0.2: +- Rename mainline handler function from _main() to main(). I can + conceive of it being called from externally. + +### v1.0.1: +- Add an optimization for Windows to allow the optional + specification of a list of exts to consider when searching the + path. + +### v1.0.0: +- Simpler interface: What was which() is now called whichgen() -- it + is a generator of matches. The simpler which() and whichall() + non-generator interfaces were added. + +### v0.8.1: +- API change: 0.8.0's API change making "verbose" output the default + was a mistake -- it breaks backward compatibility for existing + uses of which in scripts. This makes verbose, once again, optional + but NOT the default. + +### v0.8.0: +- bug fix: "App Paths" lookup had been crippled in 0.7.0. Restore that. +- feature/module API change: Now print out (and return for the module + interface) from where a match was found, e.g. "(from PATH element 3)". + The module interfaces now returns (match, from-where) tuples. +- bug fix: --path argument was broken (-p shortform was fine) + +### v0.7.0: +- bug fix: Handle "App Paths" registered executable that does not + exist. +- feature: Allow an alternate PATH to be specified via 'path' + optional argument to which.which() and via -p|--path command line + option. + +### v0.6.1: +- first public release + diff --git a/python/which/TODO.txt b/python/which/TODO.txt new file mode 100644 index 0000000000000..6df2de7f70ac5 --- /dev/null +++ b/python/which/TODO.txt @@ -0,0 +1,113 @@ +# High Priority + +- Figure out the script story on the various platforms. On Windows, look into + the launcher thing that effbot has. Unix, don't install the script my + default. They can always do "python -m which ..." with Python >= 2.4. + Suggest an alias that some folks might want to use for that. + + +# Medium Priority + +- define __all__? +- improve test suite +- test with other versions of Python +- get the PATHEXT attached extension to reflect the actual canonical + case of file matches on Windows, currently the extension from PATHEXT + is always uppercase +- What to do with Change 145624 by shanec. It is a bit of a + bastardization. Maybe allow this with a special option to allow the change + in semantics. + + > Change 145624 by shanec@shanec-ocelotl on 2005/05/24 16:51:55 + > + > make which work better on OSX + > - add support for searching /Applications and /Network/Applications + > - add support for .app bundles + > + > Affected files ... + > + > ... //depot/main/Apps/Komodo-devel/src/python-sitelib/which.py#7 edit + > + > Differences ... + > + > ==== //depot/main/Apps/Komodo-devel/src/python-sitelib/which.py#7 (text) ==== + > + > @@ -126,10 +126,11 @@ + > sys.stderr.write("duplicate: %s (%s)\n" % potential) + > return None + > else: + > - if not stat.S_ISREG(os.stat(potential[0]).st_mode): + > + darwinApp = sys.platform == 'darwin' and potential[0][-4:]=='.app' + > + if not darwinApp and not stat.S_ISREG(os.stat(potential[0]).st_mode): + > if verbose: + > sys.stderr.write("not a regular file: %s (%s)\n" % potential) + > - elif not os.access(potential[0], os.X_OK): + > + elif not darwinApp and not os.access(potential[0], os.X_OK): + > if verbose: + > sys.stderr.write("no executable access: %s (%s)\n"\ + > % potential) + > @@ -166,6 +167,9 @@ + > path = os.environ.get("PATH", "").split(os.pathsep) + > if sys.platform.startswith("win"): + > path.insert(0, os.curdir) # implied by Windows shell + > + if sys.platform == 'darwin': + > + path.insert(0, '/Network/Applications') + > + path.insert(0, '/Applications') + > else: + > usingGivenPath = 1 + > + > @@ -182,6 +186,9 @@ + > exts = ['.COM', '.EXE', '.BAT'] + > elif not isinstance(exts, list): + > raise TypeError("'exts' argument must be a list or None") + > + elif sys.platform == 'darwin': + > + if exts is None: + > + exts = ['.app'] + > else: + > if exts is not None: + > raise WhichError("'exts' argument is not supported on "\ + > @@ -202,7 +209,8 @@ + > for ext in ['']+exts: + > absName = os.path.abspath( + > os.path.normpath(os.path.join(dirName, command+ext))) + > - if os.path.isfile(absName): + > + if os.path.isfile(absName) or (sys.platform == 'darwin' and \ + > + absName[-4:]=='.app' and os.path.isdir(absName)): + > if usingGivenPath: + > fromWhere = "from given path element %d" % i + > elif not sys.platform.startswith("win"): + + Here is a start with slight improvements: + + > Index: which.py + > =================================================================== + > --- which.py (revision 270) + > +++ which.py (working copy) + > @@ -126,9 +126,18 @@ + > sys.stderr.write("duplicate: %s (%s)\n" % potential) + > return None + > else: + > - if not stat.S_ISREG(os.stat(potential[0]).st_mode): + > + st_mode = os.stat(potential[0]).st_mode + > + isMacAppBundle = sys.platform == "darwin" \ + > + and potential[0].endswith(".app") \ + > + and stat.S_ISDIR(st_mode) + > + if not isMacAppBundle and not stat.S_ISREG(st_mode): + > if verbose: + > - sys.stderr.write("not a regular file: %s (%s)\n" % potential) + > + if sys.platform == "darwin": + > + sys.stderr.write("not a regular file or .app bundle: " + > + "%s (%s)\n" % potential) + > + else: + > + sys.stderr.write("not a regular file: %s (%s)\n" + > + % potential) + > elif not os.access(potential[0], os.X_OK): + > if verbose: + > sys.stderr.write("no executable access: %s (%s)\n"\ + + +# Low Priority + +- have a version for pre-generators (i.e. Python 2.1) +- add a "logging" interface + diff --git a/python/which/build.py b/python/which/build.py new file mode 100644 index 0000000000000..3c8f09d39e1af --- /dev/null +++ b/python/which/build.py @@ -0,0 +1,442 @@ +#!/usr/bin/env python +# Copyright (c) 2002-2005 ActiveState +# See LICENSE.txt for license details. + +""" + which.py dev build script + + Usage: + python build.py [...] [...] + + Options: + --help, -h Print this help and exit. + --targets, -t List all available targets. + + This is the primary build script for the which.py project. It exists + to assist in building, maintaining, and distributing this project. + + It is intended to have Makefile semantics. I.e. 'python build.py' + will build execute the default target, 'python build.py foo' will + build target foo, etc. However, there is no intelligent target + interdependency tracking (I suppose I could do that with function + attributes). +""" + +import os +from os.path import basename, dirname, splitext, isfile, isdir, exists, \ + join, abspath, normpath +import sys +import getopt +import types +import getpass +import shutil +import glob +import logging +import re + + + +#---- exceptions + +class Error(Exception): + pass + + + +#---- globals + +log = logging.getLogger("build") + + + + +#---- globals + +_project_name_ = "which" + + + +#---- internal support routines + +def _get_trentm_com_dir(): + """Return the path to the local trentm.com source tree.""" + d = normpath(join(dirname(__file__), os.pardir, "trentm.com")) + if not isdir(d): + raise Error("could not find 'trentm.com' src dir at '%s'" % d) + return d + +def _get_local_bits_dir(): + import imp + info = imp.find_module("tmconfig", [_get_trentm_com_dir()]) + tmconfig = imp.load_module("tmconfig", *info) + return tmconfig.bitsDir + +def _get_project_bits_dir(): + d = normpath(join(dirname(__file__), "bits")) + return d + +def _get_project_version(): + import imp, os + data = imp.find_module(_project_name_, [os.path.dirname(__file__)]) + mod = imp.load_module(_project_name_, *data) + return mod.__version__ + + +# Recipe: run (0.5.1) in /Users/trentm/tm/recipes/cookbook +_RUN_DEFAULT_LOGSTREAM = ("RUN", "DEFAULT", "LOGSTREAM") +def __run_log(logstream, msg, *args, **kwargs): + if not logstream: + pass + elif logstream is _RUN_DEFAULT_LOGSTREAM: + try: + log.debug(msg, *args, **kwargs) + except NameError: + pass + else: + logstream(msg, *args, **kwargs) + +def _run(cmd, logstream=_RUN_DEFAULT_LOGSTREAM): + """Run the given command. + + "cmd" is the command to run + "logstream" is an optional logging stream on which to log the command. + If None, no logging is done. If unspecifed, this looks for a Logger + instance named 'log' and logs the command on log.debug(). + + Raises OSError is the command returns a non-zero exit status. + """ + __run_log(logstream, "running '%s'", cmd) + retval = os.system(cmd) + if hasattr(os, "WEXITSTATUS"): + status = os.WEXITSTATUS(retval) + else: + status = retval + if status: + #TODO: add std OSError attributes or pick more approp. exception + raise OSError("error running '%s': %r" % (cmd, status)) + +def _run_in_dir(cmd, cwd, logstream=_RUN_DEFAULT_LOGSTREAM): + old_dir = os.getcwd() + try: + os.chdir(cwd) + __run_log(logstream, "running '%s' in '%s'", cmd, cwd) + _run(cmd, logstream=None) + finally: + os.chdir(old_dir) + + +# Recipe: rmtree (0.5) in /Users/trentm/tm/recipes/cookbook +def _rmtree_OnError(rmFunction, filePath, excInfo): + if excInfo[0] == OSError: + # presuming because file is read-only + os.chmod(filePath, 0777) + rmFunction(filePath) +def _rmtree(dirname): + import shutil + shutil.rmtree(dirname, 0, _rmtree_OnError) + + +# Recipe: pretty_logging (0.1) in /Users/trentm/tm/recipes/cookbook +class _PerLevelFormatter(logging.Formatter): + """Allow multiple format string -- depending on the log level. + + A "fmtFromLevel" optional arg is added to the constructor. It can be + a dictionary mapping a log record level to a format string. The + usual "fmt" argument acts as the default. + """ + def __init__(self, fmt=None, datefmt=None, fmtFromLevel=None): + logging.Formatter.__init__(self, fmt, datefmt) + if fmtFromLevel is None: + self.fmtFromLevel = {} + else: + self.fmtFromLevel = fmtFromLevel + def format(self, record): + record.levelname = record.levelname.lower() + if record.levelno in self.fmtFromLevel: + #XXX This is a non-threadsafe HACK. Really the base Formatter + # class should provide a hook accessor for the _fmt + # attribute. *Could* add a lock guard here (overkill?). + _saved_fmt = self._fmt + self._fmt = self.fmtFromLevel[record.levelno] + try: + return logging.Formatter.format(self, record) + finally: + self._fmt = _saved_fmt + else: + return logging.Formatter.format(self, record) + +def _setup_logging(): + hdlr = logging.StreamHandler() + defaultFmt = "%(name)s: %(levelname)s: %(message)s" + infoFmt = "%(name)s: %(message)s" + fmtr = _PerLevelFormatter(fmt=defaultFmt, + fmtFromLevel={logging.INFO: infoFmt}) + hdlr.setFormatter(fmtr) + logging.root.addHandler(hdlr) + log.setLevel(logging.INFO) + + +def _getTargets(): + """Find all targets and return a dict of targetName:targetFunc items.""" + targets = {} + for name, attr in sys.modules[__name__].__dict__.items(): + if name.startswith('target_'): + targets[ name[len('target_'):] ] = attr + return targets + +def _listTargets(targets): + """Pretty print a list of targets.""" + width = 77 + nameWidth = 15 # min width + for name in targets.keys(): + nameWidth = max(nameWidth, len(name)) + nameWidth += 2 # space btwn name and doc + format = "%%-%ds%%s" % nameWidth + print format % ("TARGET", "DESCRIPTION") + for name, func in sorted(targets.items()): + doc = _first_paragraph(func.__doc__ or "", True) + if len(doc) > (width - nameWidth): + doc = doc[:(width-nameWidth-3)] + "..." + print format % (name, doc) + + +# Recipe: first_paragraph (1.0.1) in /Users/trentm/tm/recipes/cookbook +def _first_paragraph(text, join_lines=False): + """Return the first paragraph of the given text.""" + para = text.lstrip().split('\n\n', 1)[0] + if join_lines: + lines = [line.strip() for line in para.splitlines(0)] + para = ' '.join(lines) + return para + + + +#---- build targets + +def target_default(): + target_all() + +def target_all(): + """Build all release packages.""" + log.info("target: default") + if sys.platform == "win32": + target_launcher() + target_sdist() + target_webdist() + + +def target_clean(): + """remove all build/generated bits""" + log.info("target: clean") + if sys.platform == "win32": + _run("nmake -f Makefile.win clean") + + ver = _get_project_version() + dirs = ["dist", "build", "%s-%s" % (_project_name_, ver)] + for d in dirs: + print "removing '%s'" % d + if os.path.isdir(d): _rmtree(d) + + patterns = ["*.pyc", "*~", "MANIFEST", + os.path.join("test", "*~"), + os.path.join("test", "*.pyc"), + ] + for pattern in patterns: + for file in glob.glob(pattern): + print "removing '%s'" % file + os.unlink(file) + + +def target_launcher(): + """Build the Windows launcher executable.""" + log.info("target: launcher") + assert sys.platform == "win32", "'launcher' target only supported on Windows" + _run("nmake -f Makefile.win") + + +def target_docs(): + """Regenerate some doc bits from project-info.xml.""" + log.info("target: docs") + _run("projinfo -f project-info.xml -R -o README.txt --force") + _run("projinfo -f project-info.xml --index-markdown -o index.markdown --force") + + +def target_sdist(): + """Build a source distribution.""" + log.info("target: sdist") + target_docs() + bitsDir = _get_project_bits_dir() + _run("python setup.py sdist -f --formats zip -d %s" % bitsDir, + log.info) + + +def target_webdist(): + """Build a web dist package. + + "Web dist" packages are zip files with '.web' package. All files in + the zip must be under a dir named after the project. There must be a + webinfo.xml file at /webinfo.xml. This file is "defined" + by the parsing in trentm.com/build.py. + """ + assert sys.platform != "win32", "'webdist' not implemented for win32" + log.info("target: webdist") + bitsDir = _get_project_bits_dir() + buildDir = join("build", "webdist") + distDir = join(buildDir, _project_name_) + if exists(buildDir): + _rmtree(buildDir) + os.makedirs(distDir) + + target_docs() + + # Copy the webdist bits to the build tree. + manifest = [ + "project-info.xml", + "index.markdown", + "LICENSE.txt", + "which.py", + "logo.jpg", + ] + for src in manifest: + if dirname(src): + dst = join(distDir, dirname(src)) + os.makedirs(dst) + else: + dst = distDir + _run("cp %s %s" % (src, dst)) + + # Zip up the webdist contents. + ver = _get_project_version() + bit = abspath(join(bitsDir, "%s-%s.web" % (_project_name_, ver))) + if exists(bit): + os.remove(bit) + _run_in_dir("zip -r %s %s" % (bit, _project_name_), buildDir, log.info) + + +def target_install(): + """Use the setup.py script to install.""" + log.info("target: install") + _run("python setup.py install") + + +def target_upload_local(): + """Update release bits to *local* trentm.com bits-dir location. + + This is different from the "upload" target, which uploads release + bits remotely to trentm.com. + """ + log.info("target: upload_local") + assert sys.platform != "win32", "'upload_local' not implemented for win32" + + ver = _get_project_version() + localBitsDir = _get_local_bits_dir() + uploadDir = join(localBitsDir, _project_name_, ver) + + bitsPattern = join(_get_project_bits_dir(), + "%s-*%s*" % (_project_name_, ver)) + bits = glob.glob(bitsPattern) + if not bits: + log.info("no bits matching '%s' to upload", bitsPattern) + else: + if not exists(uploadDir): + os.makedirs(uploadDir) + for bit in bits: + _run("cp %s %s" % (bit, uploadDir), log.info) + + +def target_upload(): + """Upload binary and source distribution to trentm.com bits + directory. + """ + log.info("target: upload") + + ver = _get_project_version() + bitsDir = _get_project_bits_dir() + bitsPattern = join(bitsDir, "%s-*%s*" % (_project_name_, ver)) + bits = glob.glob(bitsPattern) + if not bits: + log.info("no bits matching '%s' to upload", bitsPattern) + return + + # Ensure have all the expected bits. + expectedBits = [ + re.compile("%s-.*\.zip$" % _project_name_), + re.compile("%s-.*\.web$" % _project_name_) + ] + for expectedBit in expectedBits: + for bit in bits: + if expectedBit.search(bit): + break + else: + raise Error("can't find expected bit matching '%s' in '%s' dir" + % (expectedBit.pattern, bitsDir)) + + # Upload the bits. + user = "trentm" + host = "trentm.com" + remoteBitsBaseDir = "~/data/bits" + remoteBitsDir = join(remoteBitsBaseDir, _project_name_, ver) + if sys.platform == "win32": + ssh = "plink" + scp = "pscp -unsafe" + else: + ssh = "ssh" + scp = "scp" + _run("%s %s@%s 'mkdir -p %s'" % (ssh, user, host, remoteBitsDir), log.info) + for bit in bits: + _run("%s %s %s@%s:%s" % (scp, bit, user, host, remoteBitsDir), + log.info) + + +def target_check_version(): + """grep for version strings in source code + + List all things that look like version strings in the source code. + Used for checking that versioning is updated across the board. + """ + sources = [ + "which.py", + "project-info.xml", + ] + pattern = r'[0-9]\+\(\.\|, \)[0-9]\+\(\.\|, \)[0-9]\+' + _run('grep -n "%s" %s' % (pattern, ' '.join(sources)), None) + + + +#---- mainline + +def build(targets=[]): + log.debug("build(targets=%r)" % targets) + available = _getTargets() + if not targets: + if available.has_key('default'): + return available['default']() + else: + log.warn("No default target available. Doing nothing.") + else: + for target in targets: + if available.has_key(target): + retval = available[target]() + if retval: + raise Error("Error running '%s' target: retval=%s"\ + % (target, retval)) + else: + raise Error("Unknown target: '%s'" % target) + +def main(argv): + _setup_logging() + + # Process options. + optlist, targets = getopt.getopt(argv[1:], 'ht', ['help', 'targets']) + for opt, optarg in optlist: + if opt in ('-h', '--help'): + sys.stdout.write(__doc__ + '\n') + return 0 + elif opt in ('-t', '--targets'): + return _listTargets(_getTargets()) + + return build(targets) + +if __name__ == "__main__": + sys.exit( main(sys.argv) ) + diff --git a/python/which/launcher.cpp b/python/which/launcher.cpp new file mode 100644 index 0000000000000..ae1d30708b2ef --- /dev/null +++ b/python/which/launcher.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2002-2003 ActiveState Corp. + * Author: Trent Mick (TrentM@ActiveState.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Console launch executable. + * + * This program exists solely to launch: + * python /.py + * on Windows. ".py" must be in the same directory. + * + * Rationale: + * - On some Windows flavours .py *can* be put on the PATHEXT to be + * able to find ".py" if it is on the PATH. This is fine + * until you need shell redirection to work. It does NOT for + * extensions to PATHEXT. Redirection *does* work for "python + *