2 Miscellaneous utility functions and classes.
6 from wizard.util import *
18 class ChangeDirectory(object):
20 Context for temporarily changing the working directory.
22 >>> with ChangeDirectory("/tmp"):
26 def __init__(self, dir):
30 self.olddir = os.getcwd()
32 def __exit__(self, *args):
35 class Counter(object):
37 Object for counting different values when you don't know what
38 they are a priori. Supports index access and iteration.
40 >>> counter = Counter()
41 >>> counter.count("foo")
42 >>> print counter["foo"]
47 def count(self, value):
48 """Increments count for ``value``."""
49 self.dict.setdefault(value, 0)
51 def __getitem__(self, key):
54 return self.dict.__iter__()
58 A map function for dictionaries. Only changes values.
60 >>> dictmap(lambda x: x + 2, {'a': 1, 'b': 2})
63 return dict((k,f(v)) for k,v in d.items())
67 A map function for dictionaries that passes key and value.
69 >>> dictkmap(lambda x, y: x + y, {1: 4, 3: 4})
72 return dict((k,f(k,v)) for k,v in d.items())
74 def get_exception_name(output):
76 Reads the traceback from a Python program and grabs the
77 fully qualified exception name.
79 lines = output.split("\n")
80 for line in lines[1:]: # skip the "traceback" line
82 if line[0] == ' ': continue
89 """Finds the uid of the person who owns this directory."""
90 return os.stat(dir).st_uid
92 def get_dir_owner(dir = "."):
94 Finds the name of the locker this directory is in.
98 This function uses the passwd database and thus
99 only works on scripts servers when querying directories
102 pwentry = pwd.getpwuid(get_dir_uid(dir))
103 # XXX: Error handling!
104 return pwentry.pw_name
107 """Returns the commit ID of the current Wizard install."""
108 # If you decide to convert this to use wizard.shell, be warned
109 # that there is a circular dependency, so this function would
110 # probably have to live somewhere else, probably wizard.git
111 wizard_git = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), ".git")
112 return subprocess.Popen(["git", "--git-dir=" + wizard_git, "rev-parse", "HEAD"], stdout=subprocess.PIPE).communicate()[0].rstrip()
114 def get_operator_info():
116 Returns tuple of ``(realname, email)`` about the person running
117 the script. If run from a scripts server, get info from Hesiod.
118 Otherwise, use the passwd database (email generated probably won't
119 actually accept mail). Useful when generating commit messages.
121 username = get_operator_name_from_gssapi()
124 hesinfo = subprocess.Popen(["hesinfo", username, "passwd"],stdout=subprocess.PIPE).communicate()[0]
125 fields = hesinfo.partition(",")[0]
126 realname = fields.rpartition(":")[2]
127 return realname, username + "@mit.edu"
129 # more traditional approach, but the email probably doesn't work
132 # since root isn't actually a useful designation, but maybe
133 # SUDO_USER contains something helpful
134 sudo_user = os.getenv("SUDO_USER")
137 pwdentry = pwd.getpwnam(sudo_user)
139 pwdentry = pwd.getpwuid(uid)
140 # XXX: error checking might be nice
141 # We follow the Ubuntu convention of gecos being a comma split field
142 # with the person's realname being the first entry.
143 return pwdentry.pw_gecos.split(",")[0], pwdentry.pw_name + "@" + socket.gethostname()
145 def get_operator_git():
147 Returns ``Real Name <username@mit.edu>`` suitable for use in
148 Git ``Something-by:`` string.
150 return "%s <%s>" % get_operator_info()
152 def get_operator_name_from_gssapi():
154 Returns username of the person operating this script based
155 off of the :envvar:`SSH_GSSAPI_NAME` environment variable.
159 :envvar:`SSH_GSSAPI_NAME` is not set by a vanilla OpenSSH
160 distributions. Scripts servers are patched to support this
161 environment variable.
163 principal = os.getenv("SSH_GSSAPI_NAME")
166 instance, _, _ = principal.partition("@")
167 if instance.endswith("/root"):
168 username, _, _ = principal.partition("/")
173 def set_operator_env():
175 Sets :envvar:`GIT_COMMITTER_NAME` and :envvar:`GIT_COMMITTER_EMAIL`
176 environment variables if applicable. Does nothing if
177 :func:`get_operator_info` throws :exc:`NoOperatorInfo`.
180 op_realname, op_email = get_operator_info()
181 os.putenv("GIT_COMMITTER_NAME", op_realname)
182 os.putenv("GIT_COMMITTER_EMAIL", op_email)
183 except NoOperatorInfo:
186 def set_author_env():
188 Sets :envvar:`GIT_AUTHOR_NAME` and :envvar:`GIT_AUTHOR_EMAIL` environment
189 variables if applicable. Does nothing if :func:`get_dir_owner` fails.
192 # XXX: should check if the directory is in AFS, and if not, use
193 # a more traditional metric
194 lockername = get_dir_owner()
195 os.putenv("GIT_AUTHOR_NAME", "%s locker" % lockername)
196 os.putenv("GIT_AUTHOR_EMAIL", "%s@scripts.mit.edu" % lockername)
197 except KeyError: # XXX: This doesn't actually make sense
201 """Sets all appropriate environment variables for Git commits."""
205 def get_git_footer():
206 """Returns strings for placing in Git log info about Wizard."""
207 return "\n".join(["Wizard-revision: %s" % get_revision()
208 ,"Wizard-args: %s" % " ".join(sys.argv)
211 class NoOperatorInfo(wizard.Error):
212 """No information could be found about the operator from Kerberos."""