[Zope-CVS] CVS: CVSROOT - adjustlinks.py:1.8
Ken Manheimer
klm at zope.com
Thu Jun 17 17:45:25 EDT 2004
Update of /cvs-repository/CVSROOT
In directory cvs.zope.org:/tmp/cvs-serv30665
Modified Files:
adjustlinks.py
Log Message:
Convert to new, RepoUtils-based code packaging.
=== CVSROOT/adjustlinks.py 1.7 => 1.8 ===
--- CVSROOT/adjustlinks.py:1.7 Wed Oct 22 11:50:08 2003
+++ CVSROOT/adjustlinks.py Thu Jun 17 17:44:54 2004
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/bin/env python
"""Adjust symlinks in the repository according to the repolinks recipe file.
@@ -10,308 +10,11 @@
LinkManager.assert_map() for specifics.
Errors - illegal or invalid paths, collisions, etc - are reported to
-stderr."""
+stderr.
-# Name of the recipe file - to be found in same directory as the script:
-RECIPE_NAME = 'repolinks'
-RESULTS_TO = 'klm at digicool.com'
-QUIET = 0
+See RepoUtils.postcommit and .adjustlinks for more details (generally
+installed in the python site-packages directory)."""
-import sys, os, stat
-import string
-SCRIPT_DIR = os.path.abspath(os.path.split(sys.argv[0])[0])
-# Assume the repository root dir contains the CVSROOT script dir.
-REPO_ROOT = os.path.split(SCRIPT_DIR)[0]
-RECIPE_PATH = os.path.join(SCRIPT_DIR, RECIPE_NAME)
+from RepoUtils.adjustlinks import cvs_adjustlinks
-EMPTY_DIR = REPO_ROOT + "/CVSROOT/Emptydir"
-
-def main(argv=None):
- lm = LinkManager()
- lm.assess_existing_links()
- lm.assert_map()
-
-class LinkManager:
- """Maintain and implement repository symlink maps."""
-
- def __init__(self):
- """Assemble data structures."""
- # [(from, to, lineno), ...]
- self._recipe_lines = self.get_recipe_lines()
- # {actual_path: actual_target}
- self._fslinks = {}
-
- def assert_map(self,
- isdir=os.path.isdir, isfile=os.path.isfile,
- islink=os.path.islink, exists=os.path.exists,
- abspath=os.path.abspath, split=os.path.split,
- readlink=os.readlink, unlink=os.unlink, pjoin=os.path.join,
- symlink=os.symlink, lstat=os.lstat, ST_INO=stat.ST_INO):
- """Impose the links specified by the recipe, removing preexisting
- links that are not specified in the map. Specifically:
-
- - Add prescribed links that are absent, when there's no non-link
- file in the way.
-
- - Adjust already-existing links that don't point to the prescribed
- place - so we can use the file to redirect existing links.
-
- - Remove links that are not prescribed.
-
- - Do nothing for links that already exist as prescribed.
-
- - Warn about prescribed links that point nowhere.
-
- - Warn about prescribed links that cannot be created because the
- indicated containing dir does not exist.
-
- - Reiterate attempts until all are done, or no more progress is
- being made - so it's not a problem for links to be dependent on
- others in order to be situated.
-
- - Delete prescribed links that point outside the repository.
-
- - Produce output about all the actions."""
-
- link2inode = self._fslinks
- inode2link = {}
- for k, v in link2inode.items(): inode2link[v] = k
-
- # Confirm or create the prescribed links:
-
- pending = self._recipe_lines
- # Collect accounted-for links inodes, to prevent deletion in cleanup:
- done = []
- retargeted = []
- resolvedsome = 1
-
- # Since links can depend on eachother, we repeatedly loop as long
- # as stuff is pending and we're making progress:
- while resolvedsome and pending:
-
- resolvedsome = 0
- doing = pending
- pending = []
- for i in doing:
- link, target, lineno = i
-
- # Is it already there, as a link?
- if islink(link):
- resolvedsome = 1
- oldtarget = readlink(link)
- # Is the existing one pointing at the right place?
- if oldtarget != target:
- info("Retargeting existing link %s\n"
- " => %s (was %s)",
- link, target, oldtarget)
- retargeted.append(lstat(link)[ST_INO])
- unlink(link)
- symlink(target, link)
- done.append((link, target, lstat(link)[ST_INO]))
-
- # Is there a non-link in the way?
- elif exists(link):
- resolvedsome = 1
- warn("%s line %d: link blocked by non-link %s",
- RECIPE_NAME, lineno, link)
-
- # Is the directory containing it there, to allow creating it?
- elif isdir(split(link)[0]):
- info("Creating link:\n %s => %s", link, target)
- symlink(target, link)
- resolvedsome = 1
- done.append((link, target, lstat(link)[ST_INO]))
-
- # Try again next time, if we're making progress - the
- # directory may eventually be established by another link:
- else:
- pending.append(i)
-
- # Warn about infeasible links:
- for link, target, lineno in pending:
- warn("%s line %d: failed to place link %s",
- RECIPE_NAME, lineno, link)
-
- # Cleanup:
- # - Remove any links that point outside the repository hierarchy
- # - Warn about links that don't point anywhere valid
- # - Remove obsolete links
-
- rootlen = len(REPO_ROOT)
- for link, target, inode in done:
-
- # Remove links that point outside the repository hierarchy:
- if abspath(pjoin(link, target))[:rootlen] != REPO_ROOT:
- warn("Removing illegal link to outside repository:\n"
- " %s => %s",
- link, target)
- unlink(link)
-
- elif not exists(link):
- warn("Orphaned link - points at nothing:\n %s => %s",
- link, readlink(link))
-
- # Cull accounted-for inode2link links, preparing for next step.
- if inode2link.has_key(inode):
- del inode2link[inode]
-
- # Most links still registered in inode2link are obsolete - remove them:
- for inode, link in inode2link.items():
- if inode not in retargeted:
- info("Removing obsolete link %s", link)
- unlink(link)
-
- def get_recipe_lines(self,
- strip=string.strip, split=string.split,
- pjoin=os.path.join, abspath=os.path.abspath,
- isabs=os.path.isabs):
- """Return massaged list of recipe lines, as tuples:
- (from, to, lineno). We omit non-recipe lines, and warn about
- malformed ones.
-
- See comments at repolinks file top for format."""
- lines = open(RECIPE_PATH).readlines()
- lineno = 0
- got = []
- for i in lines:
- lineno = lineno + 1
- i = strip(i)
- if not i or i[0] == '#':
- continue
- fields = split(i)
- if len(fields) > 2:
- warn("Skipping bad line %d - %d fields, should not exceed 2",
- lineno, len(fields))
- continue
- elif len(fields) == 1:
- # Empty target means point to EMPTY_DIR - place holder for
- # removed dirs.
- link, target = fields[0], EMPTY_DIR
- else:
- link, target = fields
- if isabs(target):
- target = REPO_ROOT + target
- delim = (not isabs(link) and os.sep) or ''
- link = REPO_ROOT + delim + link
- got.append((link, target, lineno))
- # Warn about dups:
- links = {}
- for link, target, lineno in got:
- if links.has_key(link):
- warn("Duplicate link encountered on line %d (last on %d):\n"
- " %s", lineno, links[link], link)
- links[link] = lineno
- return got
-
- def assess_existing_links(self, readlink=os.readlink, lstat=os.lstat,
- ST_INO=stat.ST_INO):
- """Walk the repository from the root, collecting existing links."""
-
- m = {}
- for i in find_fs_links(REPO_ROOT):
- m[i] = lstat(i)[ST_INO]
- self._fslinks = m
- return m
-
- def all_containers(self, path,
- exists=os.path.exists, ST_INO=stat.ST_INO):
- """Given a repo-relative path, return *all* the repository paths that
- contain it - direct containers *and* directories that container it by
- virtue of symlinks in the repolinks map.
-
- This is used in postcommit_actions to identify all the
- checkin-notification subscriber entries that qualify for a particular
- file, not just the containers obvious from the checkin path.
-
- We expect a path relative to the repository root, and return paths
- relative to the repository root."""
- # We get the inodes of all the directories on the actual (physical)
- # path, and identify all those links whose targets are one of those
- # directories.
-
- got = {path: 1}
-
- if path[:len(os.sep)] == os.sep:
- # Strip the leading '/'
- path = path[len(os.sep):]
- path = os.path.join(REPO_ROOT, path)
-
- actual_path, element_inodes = path_element_inodes(path)
- if element_inodes:
- # Include actual path (relative to actual repo root).
- got[actual_path[len(ACTUAL_REPO_ROOT())+len(os.sep):]] = 1
- for link, target, lineno in self._recipe_lines:
- if (exists(target)
- and (os.stat(target)[ST_INO] in element_inodes)):
- # (Strip the REPO_ROOT prefix.)
- got[link[len(REPO_ROOT)+len(os.sep):]] = 1
-
- return got.keys()
-
-def path_element_inodes(path, split=os.path.split,
- stat=os.stat, ST_INO=stat.ST_INO):
- """Return actual path relative to repository, and root inodes of directory
- elements leading to path's physical location.
- We return the empty list if path doesn't actually exist."""
-
- actual_path = None
- got = []
- if (os.path.exists(path) or os.path.exists(split(path)[0])):
- here = actual_path = actual_dir(path)
- while here and (here != '/'):
- got.insert(0, stat(here)[ST_INO])
- here = split(here)[0]
- return (actual_path, got)
-
-def find_fs_links(dir,
- join=os.path.join,
- isdir=os.path.isdir, islink=os.path.islink):
- """Return a breadth-first list of paths of symlinks within dir."""
- got = []
- dirs = []
-
- for f in os.listdir(dir):
- p = join(dir, f)
- if islink(p): got.append(p)
- elif isdir(p): dirs.append(p)
-
- for d in dirs:
- got.extend(find_fs_links(d))
-
- return got
-
-def actual_dir(path):
- """Return the real directory, as reported by os.getcwd from inside the dir.
-
- If the arg is not a directory, try with the final path element stripped."""
- origdir = os.getcwd()
- try:
- if os.path.isdir(path):
- os.chdir(path)
- else:
- os.chdir(os.path.split(path)[0])
- return os.getcwd()
-
- finally:
- os.chdir(origdir)
-
-_ACTUAL_REPO_ROOT = None
-def ACTUAL_REPO_ROOT():
- global _ACTUAL_REPO_ROOT
- if _ACTUAL_REPO_ROOT is None:
- _ACTUAL_REPO_ROOT = actual_dir(REPO_ROOT)
- return _ACTUAL_REPO_ROOT
-
-def info(*args):
- if not QUIET:
- apply(warn, args, {'status': "-- Info"})
-
-def warn(*args, **kw):
- status = kw.get('status', "** Warning")
- print "%s: %s" % (status, args[0] % args[1:])
-
-if __name__ == "__main__":
- print (" === %s run ===\n ...on commit of %s...\n"
- % (sys.argv[0], RECIPE_PATH))
- main(sys.argv)
- print "-- Done."
+cvs_adjustlinks(recipe_name="repolinks", quiet=0)
More information about the Zope-CVS
mailing list