Greetings. Per the setuptools docs, below is patch to add support for the Monotone revision control system[1] to setuptools. I've tested this patch lightly on Linux (Fedora Core 5) with Python 2.4.3 and Monotone 0.33. Patch is against setuptools r54707 (which I think is "head"). Apologies if this isn't the appropriate place for these patches. If that's the case and someone could advise me where they should go (setuptools documentation doesn't seem too specific) I would be grateful. Dale [1] http://monotone.ca/ -- Add support for automatically detecting files under control by Monotone (mtn_revctrl). I also renamed _default_revctrl to svn_cvs_revctrl, so that the naming could be more consistent. Clearly my patches to the documentation were a bit slapdash, simply adding Monotone to the list where appropriate; apologies. Index: setuptools/command/sdist.py =================================================================== --- setuptools/command/sdist.py (revision 54707) +++ setuptools/command/sdist.py (working copy) @@ -1,6 +1,7 @@ from distutils.command.sdist import sdist as _sdist from distutils.util import convert_path import os, re, sys, pkg_resources +import popen2 entities = [ ("<","<"), (">", ">"), (""", '"'), ("'", "'"), @@ -45,7 +46,7 @@ for item in ep.load()(dirname): yield item -def _default_revctrl(dirname=''): +def svn_cvs_revctrl(dirname=''): for path, finder in finders: path = joinpath(dirname,path) if os.path.isfile(path): @@ -53,7 +54,7 @@ if os.path.isfile(path): yield path elif os.path.isdir(path): - for item in _default_revctrl(path): + for item in svn_cvs_revctrl(path): yield item def externals_finder(dirname, filename): @@ -107,20 +108,147 @@ ] +class Monotone (object): + def __init__(self, command, capture_stderr=False): + if capture_stderr: + popen_constructor = popen2.Popen4 + else: + popen_constructor = popen2.Popen3 + mtn_executable = os.environ.get("MTN", "mtn") + popen = self._popen = popen_constructor("%s --xargs -" + % (mtn_executable,)) + popen.tochild.write(command) + popen.tochild.close() + self.readline = popen.fromchild.readline + self._closed = False + def __iter__(self): + return iter(self._popen.fromchild) + def iter_inventory(self): + "Yields valid file names from 'automate inventory' output." + for line in self: + # The specs for this are in monotone's automate.cc. Briefly: + # + # * First column is " " for unchanged, "D"eleted, or + # "R"enamed from + # * Second column is " " for unchanged, "R"enamed to, or + # "A"dded + # * Third column is " " for unchanged, "P"atched, "U"nknown, + # "I"gnored, or "M"issing (from the filesystem) + # + # Then a space, two ID numbers separated by a space, + # another space, and the file name. So we strip the first + # columns, split on space twice, and return the tail end + # of the line which should be path/file. + line = line.rstrip() + if line[0] == " " and line[2] in " P": + yield line[4:].split(" ", 2)[2] + def discard_output(self): + for line in self: + pass + def _close(self): + assert not self._closed + self._popen.fromchild.close() + result = self._popen.wait() + if os.WIFEXITED(result): + self.exit_status = (True, os.WEXITSTATUS(result)) + else: + self.exit_status = (False, result) + self._closed = True + return self.exit_status + def exited_normally(self): + if not self._closed: + self._close() + exited, code = self.exit_status + return exited and code == 0 +def acceptable_mtn_version(): + mtn = Monotone("automate interface_version", capture_stderr=True) + version = mtn.readline().strip() + if not mtn.exited_normally(): + return False + try: + major, minor = version.split(".", 1) + if 1 <= int(major) <= 4: + return True + else: + distutils.log.warn("no support for mtn automate version %r" + % (version,)) + return False + except: + distutils.log.warn("couldn't parse mtn automate version %r" + % (version,)) + return False +def find_mtn_root(a_path): + assert os.path.isabs(a_path) + assert a_path == os.path.normpath(a_path) + if os.path.isdir(os.path.join(a_path, "_MTN")): + return a_path + else: + head, tail = os.path.split(a_path) + if head != a_path: + return find_mtn_root(head) + else: + return None +def get_path_relative_to_mtn_root(mtn_root, abs_path): + common_path = os.path.commonprefix([mtn_root, abs_path]) + return abs_path[len(common_path) + 1:] +def call_with_cwd(temp_cwd, callable, *args, **kwargs): + orig_cwd = os.getcwd() + try: + try: + os.chdir(temp_cwd) + except OSError, e: + distutils.log.warn("error with mtn plug-in: %s" % (str(e),)) + return None + return callable(*args, **kwargs) + finally: + os.chdir(orig_cwd) +def mtn_revctrl(a_path): + abs_path = os.path.normpath(os.path.abspath(a_path)) + mtn_root = find_mtn_root(abs_path) + if mtn_root is None or not acceptable_mtn_version(): + return + mtn_relative_path = get_path_relative_to_mtn_root(mtn_root, abs_path) + if mtn_relative_path: + strip_before = len(mtn_relative_path) + 1 + else: + strip_before = 0 + mtn = call_with_cwd(abs_path, Monotone, "automate inventory") + if not mtn: + return + for file in mtn.iter_inventory(): + if (file.startswith(mtn_relative_path) + and file.rstrip("/") != mtn_relative_path): + yield os.path.join(a_path, file[strip_before:]) + if not mtn.exited_normally(): + distutils.log.warn("error running mtn: exited=%r status=%r" + % mtn.exit_status) + + + + + + + + + + + + + class sdist(_sdist): """Smart sdist that finds anything supported by revision control""" Index: setup.py =================================================================== --- setup.py (revision 54707) +++ setup.py (working copy) @@ -70,7 +70,8 @@ % sys.version[:3] ], "setuptools.file_finders": - ["svn_cvs = setuptools.command.sdist:_default_revctrl"], + ["svn_cvs = setuptools.command.sdist:svn_cvs_revctrl", + "mtn = setuptools.command.sdist:mtn_revctrl"], "setuptools.installation": Index: setuptools.txt =================================================================== --- setuptools.txt (revision 54707) +++ setuptools.txt (working copy) @@ -261,10 +261,10 @@ ``include_package_data`` If set to ``True``, this tells ``setuptools`` to automatically include any - data files it finds inside your package directories, that are either under - CVS or Subversion control, or which are specified by your ``MANIFEST.in`` - file. For more information, see the section below on `Including Data - Files`_. + data files it finds inside your package directories, that are under CVS, + Subversion, or Monotone control, or data files which are specified by your + ``MANIFEST.in`` file. For more information, see the section below on + `Including Data Files`_. ``exclude_package_data`` A dictionary mapping package names to lists of glob patterns that should @@ -745,10 +745,10 @@ ) This tells setuptools to install any data files it finds in your packages. The -data files must be under CVS or Subversion control, or else they must be -specified via the distutils' ``MANIFEST.in`` file. (They can also be tracked -by another revision control system, using an appropriate plugin. See the -section below on `Adding Support for Other Revision Control Systems`_ for +data files must be under CVS, Subversion, or Monotone control, or else they +must be specified via the distutils' ``MANIFEST.in`` file. (They can also be +tracked by another revision control system, using an appropriate plugin. See +the section below on `Adding Support for Other Revision Control Systems`_ for information on how to write such plugins.) If you want finer-grained control over what files are included (for example, if @@ -1429,17 +1429,17 @@ ------------------------------- ``setuptools`` enhances the distutils' default algorithm for source file -selection, so that all files managed by CVS or Subversion in your project tree -are included in any source distribution you build. This is a big improvement -over having to manually write a ``MANIFEST.in`` file and try to keep it in -sync with your project. So, if you are using CVS or Subversion, and your -source distributions only need to include files that you're tracking in -revision control, don't create a a ``MANIFEST.in`` file for your project. -(And, if you already have one, you might consider deleting it the next time -you would otherwise have to change it.) +selection, so that all files managed by CVS, Subversion, or Monotone in your +project tree are included in any source distribution you build. This is a big +improvement over having to manually write a ``MANIFEST.in`` file and try to +keep it in sync with your project. So, if you are using CVS, Subversion, or +Monotone, and your source distributions only need to include files that you're +tracking in revision control, don't create a a ``MANIFEST.in`` file for your +project. (And, if you already have one, you might consider deleting it the +next time you would otherwise have to change it.) -(NOTE: other revision control systems besides CVS and Subversion can be -supported using plugins; see the section below on `Adding Support for Other +(NOTE: other revision control systems besides CVS, Subversion, and Monotone can +be supported using plugins; see the section below on `Adding Support for Other Revision Control Systems`_ for information on how to write such plugins.) If you need to include automatically generated files, or files that are kept in @@ -2495,9 +2495,9 @@ ------------------------------------------------- If you would like to create a plugin for ``setuptools`` to find files in other -source control systems besides CVS and Subversion, you can do so by adding an -entry point to the ``setuptools.file_finders`` group. The entry point should -be a function accepting a single directory name, and should yield +source control systems besides CVS, Subversion, and Monotone, you can do so by +adding an entry point to the ``setuptools.file_finders`` group. The entry +point should be a function accepting a single directory name, and should yield all the filenames within that directory (and any subdirectories thereof) that are under revision control. Index: setuptools.egg-info/entry_points.txt =================================================================== --- setuptools.egg-info/entry_points.txt (revision 54707) +++ setuptools.egg-info/entry_points.txt (working copy) @@ -35,7 +35,8 @@ easy_install-2.3 = setuptools.command.easy_install:main [setuptools.file_finders] -svn_cvs = setuptools.command.sdist:_default_revctrl +svn_cvs = setuptools.command.sdist:svn_cvs_revctrl +mtn = setuptools.command.sdist:mtn_revctrl [distutils.setup_keywords] dependency_links = setuptools.dist:assert_string_list