Showing the changes to sys/src/cmd/python (894 of 3706 files). Show all
Unified · Split
| @@ -0,0 +1,61 @@ | |||
| 1 | This directory contains various demonstrations of what you can do with | ||
| 2 | Python. They were all written by me except where explicitly stated | ||
| 3 | otherwise -- in general, demos contributed by others ends up in the | ||
| 4 | ../Contrib directory, unless I think they're of utmost general | ||
| 5 | importance (like Matt Conway's Tk demos). | ||
| 6 | |||
| 7 | A fair number of utilities that are useful when while developing | ||
| 8 | Python code can be found in the ../Tools directory -- some of these | ||
| 9 | can also be considered good examples of how to write Python code. | ||
| 10 | |||
| 11 | Finally, in order to save disk space and net bandwidth, not all | ||
| 12 | subdirectories listed here are distributed. They are listed just | ||
| 13 | in case I change my mind about them. | ||
| 14 | |||
| 15 | |||
| 16 | cgi CGI examples (see also ../Tools/faqwiz/.) | ||
| 17 | |||
| 18 | classes Some examples of how to use classes. | ||
| 19 | |||
| 20 | comparisons A set of responses to a really old language-comparison | ||
| 21 | challenge. | ||
| 22 | |||
| 23 | curses A set of curses demos. | ||
| 24 | |||
| 25 | embed An example of embedding Python in another application | ||
| 26 | (see also pysvr). | ||
| 27 | |||
| 28 | imputil Demonstration subclasses of imputil.Importer. | ||
| 29 | |||
| 30 | md5test Test program for the optional md5 module. | ||
| 31 | |||
| 32 | metaclasses The code from the 1.5 metaclasses paper on the web. | ||
| 33 | |||
| 34 | parser Example using the parser module. | ||
| 35 | |||
| 36 | pdist Old, unfinished code messing with CVS, RCS and remote | ||
| 37 | files. | ||
| 38 | |||
| 39 | pysvr An example of embedding Python in a threaded | ||
| 40 | application. | ||
| 41 | |||
| 42 | rpc A set of classes for building clients and servers for | ||
| 43 | Sun RPC. | ||
| 44 | |||
| 45 | scripts Some useful Python scripts that I put in my bin | ||
| 46 | directory. No optional built-in modules needed. | ||
| 47 | |||
| 48 | sockets Examples for the new built-in module 'socket'. | ||
| 49 | |||
| 50 | threads Demos that use the 'thread' module. (Currently these | ||
| 51 | only run on SGIs, but this may change in the future.) | ||
| 52 | |||
| 53 | tix Demos using the Tix widget set addition to Tkinter. | ||
| 54 | |||
| 55 | tkinter Demos using the Tk interface (including Matt Conway's | ||
| 56 | excellent set of demos). | ||
| 57 | |||
| 58 | xml Some XML demos. | ||
| 59 | |||
| 60 | zlib Some demos for the zlib module (see also the standard | ||
| 61 | library module gzip.py). | ||
| @@ -0,0 +1,11 @@ | |||
| 1 | CGI Examples | ||
| 2 | ------------ | ||
| 3 | |||
| 4 | Here are some example CGI programs. For a larger example, see | ||
| 5 | ../../Tools/faqwiz/. | ||
| 6 | |||
| 7 | cgi0.sh -- A shell script to test your server is configured for CGI | ||
| 8 | cgi1.py -- A Python script to test your server is configured for CGI | ||
| 9 | cgi2.py -- A Python script showing how to parse a form | ||
| 10 | cgi3.py -- A Python script for driving an arbitrary CGI application | ||
| 11 | wiki.py -- Sample CGI application: a minimal Wiki implementation | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | #! /bin/sh | ||
| 2 | |||
| 3 | # If you can't get this to work, your web server isn't set up right | ||
| 4 | |||
| 5 | echo Content-type: text/plain | ||
| 6 | echo | ||
| 7 | echo Hello world | ||
| 8 | echo This is cgi0.sh | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | #!/usr/local/bin/python | ||
| 2 | |||
| 3 | """CGI test 1 - check server setup.""" | ||
| 4 | |||
| 5 | # Until you get this to work, your web server isn't set up right or | ||
| 6 | # your Python isn't set up right. | ||
| 7 | |||
| 8 | # If cgi0.sh works but cgi1.py doesn't, check the #! line and the file | ||
| 9 | # permissions. The docs for the cgi.py module have debugging tips. | ||
| 10 | |||
| 11 | print "Content-type: text/html" | ||
| 12 | |||
| 13 | print "<h1>Hello world</h1>" | ||
| 14 | print "<p>This is cgi1.py" | ||
| @@ -0,0 +1,22 @@ | |||
| 1 | #!/usr/local/bin/python | ||
| 2 | |||
| 3 | """CGI test 2 - basic use of cgi module.""" | ||
| 4 | |||
| 5 | import cgitb; cgitb.enable() | ||
| 6 | |||
| 7 | import cgi | ||
| 8 | |||
| 9 | def main(): | ||
| 10 | form = cgi.FieldStorage() | ||
| 11 | print "Content-type: text/html" | ||
| 12 | |||
| 13 | if not form: | ||
| 14 | print "<h1>No Form Keys</h1>" | ||
| 15 | else: | ||
| 16 | print "<h1>Form Keys</h1>" | ||
| 17 | for key in form.keys(): | ||
| 18 | value = form[key].value | ||
| 19 | print "<p>", cgi.escape(key), ":", cgi.escape(value) | ||
| 20 | |||
| 21 | if __name__ == "__main__": | ||
| 22 | main() | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | #!/usr/local/bin/python | ||
| 2 | |||
| 3 | """CGI test 3 (persistent data).""" | ||
| 4 | |||
| 5 | import cgitb; cgitb.enable() | ||
| 6 | |||
| 7 | from wiki import main | ||
| 8 | |||
| 9 | if __name__ == "__main__": | ||
| 10 | main() | ||
| @@ -0,0 +1,123 @@ | |||
| 1 | """Wiki main program. Imported and run by cgi3.py.""" | ||
| 2 | |||
| 3 | import os, re, cgi, sys, tempfile | ||
| 4 | escape = cgi.escape | ||
| 5 | |||
| 6 | def main(): | ||
| 7 | form = cgi.FieldStorage() | ||
| 8 | print "Content-type: text/html" | ||
| 9 | |||
| 10 | cmd = form.getvalue("cmd", "view") | ||
| 11 | page = form.getvalue("page", "FrontPage") | ||
| 12 | wiki = WikiPage(page) | ||
| 13 | method = getattr(wiki, 'cmd_' + cmd, None) or wiki.cmd_view | ||
| 14 | method(form) | ||
| 15 | |||
| 16 | class WikiPage: | ||
| 17 | |||
| 18 | homedir = tempfile.gettempdir() | ||
| 19 | scripturl = os.path.basename(sys.argv[0]) | ||
| 20 | |||
| 21 | def __init__(self, name): | ||
| 22 | if not self.iswikiword(name): | ||
| 23 | raise ValueError, "page name is not a wiki word" | ||
| 24 | self.name = name | ||
| 25 | self.load() | ||
| 26 | |||
| 27 | def cmd_view(self, form): | ||
| 28 | print "<h1>", escape(self.splitwikiword(self.name)), "</h1>" | ||
| 29 | print "<p>" | ||
| 30 | for line in self.data.splitlines(): | ||
| 31 | line = line.rstrip() | ||
| 32 | if not line: | ||
| 33 | print "<p>" | ||
| 34 | else: | ||
| 35 | print self.formatline(line) | ||
| 36 | print "<hr>" | ||
| 37 | print "<p>", self.mklink("edit", self.name, "Edit this page") + ";" | ||
| 38 | print self.mklink("view", "FrontPage", "go to front page") + "." | ||
| 39 | |||
| 40 | def formatline(self, line): | ||
| 41 | words = [] | ||
| 42 | for word in re.split('(\W+)', line): | ||
| 43 | if self.iswikiword(word): | ||
| 44 | if os.path.isfile(self.mkfile(word)): | ||
| 45 | word = self.mklink("view", word, word) | ||
| 46 | else: | ||
| 47 | word = self.mklink("new", word, word + "*") | ||
| 48 | else: | ||
| 49 | word = escape(word) | ||
| 50 | words.append(word) | ||
| 51 | return "".join(words) | ||
| 52 | |||
| 53 | def cmd_edit(self, form, label="Change"): | ||
| 54 | print "<h1>", label, self.name, "</h1>" | ||
| 55 | print '<form method="POST" action="%s">' % self.scripturl | ||
| 56 | s = '<textarea cols="70" rows="20" name="text">%s</textarea>' | ||
| 57 | print s % self.data | ||
| 58 | print '<input type="hidden" name="cmd" value="create">' | ||
| 59 | print '<input type="hidden" name="page" value="%s">' % self.name | ||
| 60 | print '<br>' | ||
| 61 | print '<input type="submit" value="%s Page">' % label | ||
| 62 | print "</form>" | ||
| 63 | |||
| 64 | def cmd_create(self, form): | ||
| 65 | self.data = form.getvalue("text", "").strip() | ||
| 66 | error = self.store() | ||
| 67 | if error: | ||
| 68 | print "<h1>I'm sorry. That didn't work</h1>" | ||
| 69 | print "<p>An error occurred while attempting to write the file:" | ||
| 70 | print "<p>", escape(error) | ||
| 71 | else: | ||
| 72 | # Use a redirect directive, to avoid "reload page" problems | ||
| 73 | print "<head>" | ||
| 74 | s = '<meta http-equiv="refresh" content="1; URL=%s">' | ||
| 75 | print s % (self.scripturl + "?cmd=view&page=" + self.name) | ||
| 76 | print "<head>" | ||
| 77 | print "<h1>OK</h1>" | ||
| 78 | print "<p>If nothing happens, please click here:", | ||
| 79 | print self.mklink("view", self.name, self.name) | ||
| 80 | |||
| 81 | def cmd_new(self, form): | ||
| 82 | self.cmd_edit(form, label="Create") | ||
| 83 | |||
| 84 | def iswikiword(self, word): | ||
| 85 | return re.match("[A-Z][a-z]+([A-Z][a-z]*)+", word) | ||
| 86 | |||
| 87 | def splitwikiword(self, word): | ||
| 88 | chars = [] | ||
| 89 | for c in word: | ||
| 90 | if chars and c.isupper(): | ||
| 91 | chars.append(' ') | ||
| 92 | chars.append(c) | ||
| 93 | return "".join(chars) | ||
| 94 | |||
| 95 | def mkfile(self, name=None): | ||
| 96 | if name is None: | ||
| 97 | name = self.name | ||
| 98 | return os.path.join(self.homedir, name + ".txt") | ||
| 99 | |||
| 100 | def mklink(self, cmd, page, text): | ||
| 101 | link = self.scripturl + "?cmd=" + cmd + "&page=" + page | ||
| 102 | return '<a href="%s">%s</a>' % (link, text) | ||
| 103 | |||
| 104 | def load(self): | ||
| 105 | try: | ||
| 106 | f = open(self.mkfile()) | ||
| 107 | data = f.read().strip() | ||
| 108 | f.close() | ||
| 109 | except IOError: | ||
| 110 | data = "" | ||
| 111 | self.data = data | ||
| 112 | |||
| 113 | def store(self): | ||
| 114 | data = self.data | ||
| 115 | try: | ||
| 116 | f = open(self.mkfile(), "w") | ||
| 117 | f.write(data) | ||
| 118 | if data and not data.endswith('\n'): | ||
| 119 | f.write('\n') | ||
| 120 | f.close() | ||
| 121 | return "" | ||
| 122 | except IOError, err: | ||
| 123 | return "IOError: %s" % str(err) | ||
| @@ -0,0 +1,320 @@ | |||
| 1 | # Complex numbers | ||
| 2 | # --------------- | ||
| 3 | |||
| 4 | # [Now that Python has a complex data type built-in, this is not very | ||
| 5 | # useful, but it's still a nice example class] | ||
| 6 | |||
| 7 | # This module represents complex numbers as instances of the class Complex. | ||
| 8 | # A Complex instance z has two data attribues, z.re (the real part) and z.im | ||
| 9 | # (the imaginary part). In fact, z.re and z.im can have any value -- all | ||
| 10 | # arithmetic operators work regardless of the type of z.re and z.im (as long | ||
| 11 | # as they support numerical operations). | ||
| 12 | # | ||
| 13 | # The following functions exist (Complex is actually a class): | ||
| 14 | # Complex([re [,im]) -> creates a complex number from a real and an imaginary part | ||
| 15 | # IsComplex(z) -> true iff z is a complex number (== has .re and .im attributes) | ||
| 16 | # ToComplex(z) -> a complex number equal to z; z itself if IsComplex(z) is true | ||
| 17 | # if z is a tuple(re, im) it will also be converted | ||
| 18 | # PolarToComplex([r [,phi [,fullcircle]]]) -> | ||
| 19 | # the complex number z for which r == z.radius() and phi == z.angle(fullcircle) | ||
| 20 | # (r and phi default to 0) | ||
| 21 | # exp(z) -> returns the complex exponential of z. Equivalent to pow(math.e,z). | ||
| 22 | # | ||
| 23 | # Complex numbers have the following methods: | ||
| 24 | # z.abs() -> absolute value of z | ||
| 25 | # z.radius() == z.abs() | ||
| 26 | # z.angle([fullcircle]) -> angle from positive X axis; fullcircle gives units | ||
| 27 | # z.phi([fullcircle]) == z.angle(fullcircle) | ||
| 28 | # | ||
| 29 | # These standard functions and unary operators accept complex arguments: | ||
| 30 | # abs(z) | ||
| 31 | # -z | ||
| 32 | # +z | ||
| 33 | # not z | ||
| 34 | # repr(z) == `z` | ||
| 35 | # str(z) | ||
| 36 | # hash(z) -> a combination of hash(z.re) and hash(z.im) such that if z.im is zero | ||
| 37 | # the result equals hash(z.re) | ||
| 38 | # Note that hex(z) and oct(z) are not defined. | ||
| 39 | # | ||
| 40 | # These conversions accept complex arguments only if their imaginary part is zero: | ||
| 41 | # int(z) | ||
| 42 | # long(z) | ||
| 43 | # float(z) | ||
| 44 | # | ||
| 45 | # The following operators accept two complex numbers, or one complex number | ||
| 46 | # and one real number (int, long or float): | ||
| 47 | # z1 + z2 | ||
| 48 | # z1 - z2 | ||
| 49 | # z1 * z2 | ||
| 50 | # z1 / z2 | ||
| 51 | # pow(z1, z2) | ||
| 52 | # cmp(z1, z2) | ||
| 53 | # Note that z1 % z2 and divmod(z1, z2) are not defined, | ||
| 54 | # nor are shift and mask operations. | ||
| 55 | # | ||
| 56 | # The standard module math does not support complex numbers. | ||
| 57 | # The cmath modules should be used instead. | ||
| 58 | # | ||
| 59 | # Idea: | ||
| 60 | # add a class Polar(r, phi) and mixed-mode arithmetic which | ||
| 61 | # chooses the most appropriate type for the result: | ||
| 62 | # Complex for +,-,cmp | ||
| 63 | # Polar for *,/,pow | ||
| 64 | |||
| 65 | import math | ||
| 66 | import sys | ||
| 67 | |||
| 68 | twopi = math.pi*2.0 | ||
| 69 | halfpi = math.pi/2.0 | ||
| 70 | |||
| 71 | def IsComplex(obj): | ||
| 72 | return hasattr(obj, 're') and hasattr(obj, 'im') | ||
| 73 | |||
| 74 | def ToComplex(obj): | ||
| 75 | if IsComplex(obj): | ||
| 76 | return obj | ||
| 77 | elif isinstance(obj, tuple): | ||
| 78 | return Complex(*obj) | ||
| 79 | else: | ||
| 80 | return Complex(obj) | ||
| 81 | |||
| 82 | def PolarToComplex(r = 0, phi = 0, fullcircle = twopi): | ||
| 83 | phi = phi * (twopi / fullcircle) | ||
| 84 | return Complex(math.cos(phi)*r, math.sin(phi)*r) | ||
| 85 | |||
| 86 | def Re(obj): | ||
| 87 | if IsComplex(obj): | ||
| 88 | return obj.re | ||
| 89 | return obj | ||
| 90 | |||
| 91 | def Im(obj): | ||
| 92 | if IsComplex(obj): | ||
| 93 | return obj.im | ||
| 94 | return 0 | ||
| 95 | |||
| 96 | class Complex: | ||
| 97 | |||
| 98 | def __init__(self, re=0, im=0): | ||
| 99 | _re = 0 | ||
| 100 | _im = 0 | ||
| 101 | if IsComplex(re): | ||
| 102 | _re = re.re | ||
| 103 | _im = re.im | ||
| 104 | else: | ||
| 105 | _re = re | ||
| 106 | if IsComplex(im): | ||
| 107 | _re = _re - im.im | ||
| 108 | _im = _im + im.re | ||
| 109 | else: | ||
| 110 | _im = _im + im | ||
| 111 | # this class is immutable, so setting self.re directly is | ||
| 112 | # not possible. | ||
| 113 | self.__dict__['re'] = _re | ||
| 114 | self.__dict__['im'] = _im | ||
| 115 | |||
| 116 | def __setattr__(self, name, value): | ||
| 117 | raise TypeError, 'Complex numbers are immutable' | ||
| 118 | |||
| 119 | def __hash__(self): | ||
| 120 | if not self.im: | ||
| 121 | return hash(self.re) | ||
| 122 | return hash((self.re, self.im)) | ||
| 123 | |||
| 124 | def __repr__(self): | ||
| 125 | if not self.im: | ||
| 126 | return 'Complex(%r)' % (self.re,) | ||
| 127 | else: | ||
| 128 | return 'Complex(%r, %r)' % (self.re, self.im) | ||
| 129 | |||
| 130 | def __str__(self): | ||
| 131 | if not self.im: | ||
| 132 | return repr(self.re) | ||
| 133 | else: | ||
| 134 | return 'Complex(%r, %r)' % (self.re, self.im) | ||
| 135 | |||
| 136 | def __neg__(self): | ||
| 137 | return Complex(-self.re, -self.im) | ||
| 138 | |||
| 139 | def __pos__(self): | ||
| 140 | return self | ||
| 141 | |||
| 142 | def __abs__(self): | ||
| 143 | return math.hypot(self.re, self.im) | ||
| 144 | |||
| 145 | def __int__(self): | ||
| 146 | if self.im: | ||
| 147 | raise ValueError, "can't convert Complex with nonzero im to int" | ||
| 148 | return int(self.re) | ||
| 149 | |||
| 150 | def __long__(self): | ||
| 151 | if self.im: | ||
| 152 | raise ValueError, "can't convert Complex with nonzero im to long" | ||
| 153 | return long(self.re) | ||
| 154 | |||
| 155 | def __float__(self): | ||
| 156 | if self.im: | ||
| 157 | raise ValueError, "can't convert Complex with nonzero im to float" | ||
| 158 | return float(self.re) | ||
| 159 | |||
| 160 | def __cmp__(self, other): | ||
| 161 | other = ToComplex(other) | ||
| 162 | return cmp((self.re, self.im), (other.re, other.im)) | ||
| 163 | |||
| 164 | def __rcmp__(self, other): | ||
| 165 | other = ToComplex(other) | ||
| 166 | return cmp(other, self) | ||
| 167 | |||
| 168 | def __nonzero__(self): | ||
| 169 | return not (self.re == self.im == 0) | ||
| 170 | |||
| 171 | abs = radius = __abs__ | ||
| 172 | |||
| 173 | def angle(self, fullcircle = twopi): | ||
| 174 | return (fullcircle/twopi) * ((halfpi - math.atan2(self.re, self.im)) % twopi) | ||
| 175 | |||
| 176 | phi = angle | ||
| 177 | |||
| 178 | def __add__(self, other): | ||
| 179 | other = ToComplex(other) | ||
| 180 | return Complex(self.re + other.re, self.im + other.im) | ||
| 181 | |||
| 182 | __radd__ = __add__ | ||
| 183 | |||
| 184 | def __sub__(self, other): | ||
| 185 | other = ToComplex(other) | ||
| 186 | return Complex(self.re - other.re, self.im - other.im) | ||
| 187 | |||
| 188 | def __rsub__(self, other): | ||
| 189 | other = ToComplex(other) | ||
| 190 | return other - self | ||
| 191 | |||
| 192 | def __mul__(self, other): | ||
| 193 | other = ToComplex(other) | ||
| 194 | return Complex(self.re*other.re - self.im*other.im, | ||
| 195 | self.re*other.im + self.im*other.re) | ||
| 196 | |||
| 197 | __rmul__ = __mul__ | ||
| 198 | |||
| 199 | def __div__(self, other): | ||
| 200 | other = ToComplex(other) | ||
| 201 | d = float(other.re*other.re + other.im*other.im) | ||
| 202 | if not d: raise ZeroDivisionError, 'Complex division' | ||
| 203 | return Complex((self.re*other.re + self.im*other.im) / d, | ||
| 204 | (self.im*other.re - self.re*other.im) / d) | ||
| 205 | |||
| 206 | def __rdiv__(self, other): | ||
| 207 | other = ToComplex(other) | ||
| 208 | return other / self | ||
| 209 | |||
| 210 | def __pow__(self, n, z=None): | ||
| 211 | if z is not None: | ||
| 212 | raise TypeError, 'Complex does not support ternary pow()' | ||
| 213 | if IsComplex(n): | ||
| 214 | if n.im: | ||
| 215 | if self.im: raise TypeError, 'Complex to the Complex power' | ||
| 216 | else: return exp(math.log(self.re)*n) | ||
| 217 | n = n.re | ||
| 218 | r = pow(self.abs(), n) | ||
| 219 | phi = n*self.angle() | ||
| 220 | return Complex(math.cos(phi)*r, math.sin(phi)*r) | ||
| 221 | |||
| 222 | def __rpow__(self, base): | ||
| 223 | base = ToComplex(base) | ||
| 224 | return pow(base, self) | ||
| 225 | |||
| 226 | def exp(z): | ||
| 227 | r = math.exp(z.re) | ||
| 228 | return Complex(math.cos(z.im)*r,math.sin(z.im)*r) | ||
| 229 | |||
| 230 | |||
| 231 | def checkop(expr, a, b, value, fuzz = 1e-6): | ||
| 232 | print ' ', a, 'and', b, | ||
| 233 | try: | ||
| 234 | result = eval(expr) | ||
| 235 | except: | ||
| 236 | result = sys.exc_type | ||
| 237 | print '->', result | ||
| 238 | if isinstance(result, str) or isinstance(value, str): | ||
| 239 | ok = (result == value) | ||
| 240 | else: | ||
| 241 | ok = abs(result - value) <= fuzz | ||
| 242 | if not ok: | ||
| 243 | print '!!\t!!\t!! should be', value, 'diff', abs(result - value) | ||
| 244 | |||
| 245 | def test(): | ||
| 246 | print 'test constructors' | ||
| 247 | constructor_test = ( | ||
| 248 | # "expect" is an array [re,im] "got" the Complex. | ||
| 249 | ( (0,0), Complex() ), | ||
| 250 | ( (0,0), Complex() ), | ||
| 251 | ( (1,0), Complex(1) ), | ||
| 252 | ( (0,1), Complex(0,1) ), | ||
| 253 | ( (1,2), Complex(Complex(1,2)) ), | ||
| 254 | ( (1,3), Complex(Complex(1,2),1) ), | ||
| 255 | ( (0,0), Complex(0,Complex(0,0)) ), | ||
| 256 | ( (3,4), Complex(3,Complex(4)) ), | ||
| 257 | ( (-1,3), Complex(1,Complex(3,2)) ), | ||
| 258 | ( (-7,6), Complex(Complex(1,2),Complex(4,8)) ) ) | ||
| 259 | cnt = [0,0] | ||
| 260 | for t in constructor_test: | ||
| 261 | cnt[0] += 1 | ||
| 262 | if ((t[0][0]!=t[1].re)or(t[0][1]!=t[1].im)): | ||
| 263 | print " expected", t[0], "got", t[1] | ||
| 264 | cnt[1] += 1 | ||
| 265 | print " ", cnt[1], "of", cnt[0], "tests failed" | ||
| 266 | # test operators | ||
| 267 | testsuite = { | ||
| 268 | 'a+b': [ | ||
| 269 | (1, 10, 11), | ||
| 270 | (1, Complex(0,10), Complex(1,10)), | ||
| 271 | (Complex(0,10), 1, Complex(1,10)), | ||
| 272 | (Complex(0,10), Complex(1), Complex(1,10)), | ||
| 273 | (Complex(1), Complex(0,10), Complex(1,10)), | ||
| 274 | ], | ||
| 275 | 'a-b': [ | ||
| 276 | (1, 10, -9), | ||
| 277 | (1, Complex(0,10), Complex(1,-10)), | ||
| 278 | (Complex(0,10), 1, Complex(-1,10)), | ||
| 279 | (Complex(0,10), Complex(1), Complex(-1,10)), | ||
| 280 | (Complex(1), Complex(0,10), Complex(1,-10)), | ||
| 281 | ], | ||
| 282 | 'a*b': [ | ||
| 283 | (1, 10, 10), | ||
| 284 | (1, Complex(0,10), Complex(0, 10)), | ||
| 285 | (Complex(0,10), 1, Complex(0,10)), | ||
| 286 | (Complex(0,10), Complex(1), Complex(0,10)), | ||
| 287 | (Complex(1), Complex(0,10), Complex(0,10)), | ||
| 288 | ], | ||
| 289 | 'a/b': [ | ||
| 290 | (1., 10, 0.1), | ||
| 291 | (1, Complex(0,10), Complex(0, -0.1)), | ||
| 292 | (Complex(0, 10), 1, Complex(0, 10)), | ||
| 293 | (Complex(0, 10), Complex(1), Complex(0, 10)), | ||
| 294 | (Complex(1), Complex(0,10), Complex(0, -0.1)), | ||
| 295 | ], | ||
| 296 | 'pow(a,b)': [ | ||
| 297 | (1, 10, 1), | ||
| 298 | (1, Complex(0,10), 1), | ||
| 299 | (Complex(0,10), 1, Complex(0,10)), | ||
| 300 | (Complex(0,10), Complex(1), Complex(0,10)), | ||
| 301 | (Complex(1), Complex(0,10), 1), | ||
| 302 | (2, Complex(4,0), 16), | ||
| 303 | ], | ||
| 304 | 'cmp(a,b)': [ | ||
| 305 | (1, 10, -1), | ||
| 306 | (1, Complex(0,10), 1), | ||
| 307 | (Complex(0,10), 1, -1), | ||
| 308 | (Complex(0,10), Complex(1), -1), | ||
| 309 | (Complex(1), Complex(0,10), 1), | ||
| 310 | ], | ||
| 311 | } | ||
| 312 | for expr in sorted(testsuite): | ||
| 313 | print expr + ':' | ||
| 314 | t = (expr,) | ||
| 315 | for item in testsuite[expr]: | ||
| 316 | checkop(*(t+item)) | ||
| 317 | |||
| 318 | |||
| 319 | if __name__ == '__main__': | ||
| 320 | test() | ||
| @@ -0,0 +1,222 @@ | |||
| 1 | # Class Date supplies date objects that support date arithmetic. | ||
| 2 | # | ||
| 3 | # Date(month,day,year) returns a Date object. An instance prints as, | ||
| 4 | # e.g., 'Mon 16 Aug 1993'. | ||
| 5 | # | ||
| 6 | # Addition, subtraction, comparison operators, min, max, and sorting | ||
| 7 | # all work as expected for date objects: int+date or date+int returns | ||
| 8 | # the date `int' days from `date'; date+date raises an exception; | ||
| 9 | # date-int returns the date `int' days before `date'; date2-date1 returns | ||
| 10 | # an integer, the number of days from date1 to date2; int-date raises an | ||
| 11 | # exception; date1 < date2 is true iff date1 occurs before date2 (& | ||
| 12 | # similarly for other comparisons); min(date1,date2) is the earlier of | ||
| 13 | # the two dates and max(date1,date2) the later; and date objects can be | ||
| 14 | # used as dictionary keys. | ||
| 15 | # | ||
| 16 | # Date objects support one visible method, date.weekday(). This returns | ||
| 17 | # the day of the week the date falls on, as a string. | ||
| 18 | # | ||
| 19 | # Date objects also have 4 read-only data attributes: | ||
| 20 | # .month in 1..12 | ||
| 21 | # .day in 1..31 | ||
| 22 | # .year int or long int | ||
| 23 | # .ord the ordinal of the date relative to an arbitrary staring point | ||
| 24 | # | ||
| 25 | # The Dates module also supplies function today(), which returns the | ||
| 26 | # current date as a date object. | ||
| 27 | # | ||
| 28 | # Those entranced by calendar trivia will be disappointed, as no attempt | ||
| 29 | # has been made to accommodate the Julian (etc) system. On the other | ||
| 30 | # hand, at least this package knows that 2000 is a leap year but 2100 | ||
| 31 | # isn't, and works fine for years with a hundred decimal digits <wink>. | ||
| 32 | |||
| 33 | # Tim Peters tim@ksr.com | ||
| 34 | # not speaking for Kendall Square Research Corp | ||
| 35 | |||
| 36 | # Adapted to Python 1.1 (where some hacks to overcome coercion are unnecessary) | ||
| 37 | # by Guido van Rossum | ||
| 38 | |||
| 39 | # Note that as of Python 2.3, a datetime module is included in the stardard | ||
| 40 | # library. | ||
| 41 | |||
| 42 | # vi:set tabsize=8: | ||
| 43 | |||
| 44 | _MONTH_NAMES = [ 'January', 'February', 'March', 'April', 'May', | ||
| 45 | 'June', 'July', 'August', 'September', 'October', | ||
| 46 | 'November', 'December' ] | ||
| 47 | |||
| 48 | _DAY_NAMES = [ 'Friday', 'Saturday', 'Sunday', 'Monday', | ||
| 49 | 'Tuesday', 'Wednesday', 'Thursday' ] | ||
| 50 | |||
| 51 | _DAYS_IN_MONTH = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ] | ||
| 52 | |||
| 53 | _DAYS_BEFORE_MONTH = [] | ||
| 54 | dbm = 0 | ||
| 55 | for dim in _DAYS_IN_MONTH: | ||
| 56 | _DAYS_BEFORE_MONTH.append(dbm) | ||
| 57 | dbm = dbm + dim | ||
| 58 | del dbm, dim | ||
| 59 | |||
| 60 | _INT_TYPES = type(1), type(1L) | ||
| 61 | |||
| 62 | def _is_leap(year): # 1 if leap year, else 0 | ||
| 63 | if year % 4 != 0: return 0 | ||
| 64 | if year % 400 == 0: return 1 | ||
| 65 | return year % 100 != 0 | ||
| 66 | |||
| 67 | def _days_in_year(year): # number of days in year | ||
| 68 | return 365 + _is_leap(year) | ||
| 69 | |||
| 70 | def _days_before_year(year): # number of days before year | ||
| 71 | return year*365L + (year+3)/4 - (year+99)/100 + (year+399)/400 | ||
| 72 | |||
| 73 | def _days_in_month(month, year): # number of days in month of year | ||
| 74 | if month == 2 and _is_leap(year): return 29 | ||
| 75 | return _DAYS_IN_MONTH[month-1] | ||
| 76 | |||
| 77 | def _days_before_month(month, year): # number of days in year before month | ||
| 78 | return _DAYS_BEFORE_MONTH[month-1] + (month > 2 and _is_leap(year)) | ||
| 79 | |||
| 80 | def _date2num(date): # compute ordinal of date.month,day,year | ||
| 81 | return _days_before_year(date.year) + \ | ||
| 82 | _days_before_month(date.month, date.year) + \ | ||
| 83 | date.day | ||
| 84 | |||
| 85 | _DI400Y = _days_before_year(400) # number of days in 400 years | ||
| 86 | |||
| 87 | def _num2date(n): # return date with ordinal n | ||
| 88 | if type(n) not in _INT_TYPES: | ||
| 89 | raise TypeError, 'argument must be integer: %r' % type(n) | ||
| 90 | |||
| 91 | ans = Date(1,1,1) # arguments irrelevant; just getting a Date obj | ||
| 92 | del ans.ord, ans.month, ans.day, ans.year # un-initialize it | ||
| 93 | ans.ord = n | ||
| 94 | |||
| 95 | n400 = (n-1)/_DI400Y # # of 400-year blocks preceding | ||
| 96 | year, n = 400 * n400, n - _DI400Y * n400 | ||
| 97 | more = n / 365 | ||
| 98 | dby = _days_before_year(more) | ||
| 99 | if dby >= n: | ||
| 100 | more = more - 1 | ||
| 101 | dby = dby - _days_in_year(more) | ||
| 102 | year, n = year + more, int(n - dby) | ||
| 103 | |||
| 104 | try: year = int(year) # chop to int, if it fits | ||
| 105 | except (ValueError, OverflowError): pass | ||
| 106 | |||
| 107 | month = min(n/29 + 1, 12) | ||
| 108 | dbm = _days_before_month(month, year) | ||
| 109 | if dbm >= n: | ||
| 110 | month = month - 1 | ||
| 111 | dbm = dbm - _days_in_month(month, year) | ||
| 112 | |||
| 113 | ans.month, ans.day, ans.year = month, n-dbm, year | ||
| 114 | return ans | ||
| 115 | |||
| 116 | def _num2day(n): # return weekday name of day with ordinal n | ||
| 117 | return _DAY_NAMES[ int(n % 7) ] | ||
| 118 | |||
| 119 | |||
| 120 | class Date: | ||
| 121 | def __init__(self, month, day, year): | ||
| 122 | if not 1 <= month <= 12: | ||
| 123 | raise ValueError, 'month must be in 1..12: %r' % (month,) | ||
| 124 | dim = _days_in_month(month, year) | ||
| 125 | if not 1 <= day <= dim: | ||
| 126 | raise ValueError, 'day must be in 1..%r: %r' % (dim, day) | ||
| 127 | self.month, self.day, self.year = month, day, year | ||
| 128 | self.ord = _date2num(self) | ||
| 129 | |||
| 130 | # don't allow setting existing attributes | ||
| 131 | def __setattr__(self, name, value): | ||
| 132 | if self.__dict__.has_key(name): | ||
| 133 | raise AttributeError, 'read-only attribute ' + name | ||
| 134 | self.__dict__[name] = value | ||
| 135 | |||
| 136 | def __cmp__(self, other): | ||
| 137 | return cmp(self.ord, other.ord) | ||
| 138 | |||
| 139 | # define a hash function so dates can be used as dictionary keys | ||
| 140 | def __hash__(self): | ||
| 141 | return hash(self.ord) | ||
| 142 | |||
| 143 | # print as, e.g., Mon 16 Aug 1993 | ||
| 144 | def __repr__(self): | ||
| 145 | return '%.3s %2d %.3s %r' % ( | ||
| 146 | self.weekday(), | ||
| 147 | self.day, | ||
| 148 | _MONTH_NAMES[self.month-1], | ||
| 149 | self.year) | ||
| 150 | |||
| 151 | # Python 1.1 coerces neither int+date nor date+int | ||
| 152 | def __add__(self, n): | ||
| 153 | if type(n) not in _INT_TYPES: | ||
| 154 | raise TypeError, 'can\'t add %r to date' % type(n) | ||
| 155 | return _num2date(self.ord + n) | ||
| 156 | __radd__ = __add__ # handle int+date | ||
| 157 | |||
| 158 | # Python 1.1 coerces neither date-int nor date-date | ||
| 159 | def __sub__(self, other): | ||
| 160 | if type(other) in _INT_TYPES: # date-int | ||
| 161 | return _num2date(self.ord - other) | ||
| 162 | else: | ||
| 163 | return self.ord - other.ord # date-date | ||
| 164 | |||
| 165 | # complain about int-date | ||
| 166 | def __rsub__(self, other): | ||
| 167 | raise TypeError, 'Can\'t subtract date from integer' | ||
| 168 | |||
| 169 | def weekday(self): | ||
| 170 | return _num2day(self.ord) | ||
| 171 | |||
| 172 | def today(): | ||
| 173 | import time | ||
| 174 | local = time.localtime(time.time()) | ||
| 175 | return Date(local[1], local[2], local[0]) | ||
| 176 | |||
| 177 | DateTestError = 'DateTestError' | ||
| 178 | def test(firstyear, lastyear): | ||
| 179 | a = Date(9,30,1913) | ||
| 180 | b = Date(9,30,1914) | ||
| 181 | if repr(a) != 'Tue 30 Sep 1913': | ||
| 182 | raise DateTestError, '__repr__ failure' | ||
| 183 | if (not a < b) or a == b or a > b or b != b: | ||
| 184 | raise DateTestError, '__cmp__ failure' | ||
| 185 | if a+365 != b or 365+a != b: | ||
| 186 | raise DateTestError, '__add__ failure' | ||
| 187 | if b-a != 365 or b-365 != a: | ||
| 188 | raise DateTestError, '__sub__ failure' | ||
| 189 | try: | ||
| 190 | x = 1 - a | ||
| 191 | raise DateTestError, 'int-date should have failed' | ||
| 192 | except TypeError: | ||
| 193 | pass | ||
| 194 | try: | ||
| 195 | x = a + b | ||
| 196 | raise DateTestError, 'date+date should have failed' | ||
| 197 | except TypeError: | ||
| 198 | pass | ||
| 199 | if a.weekday() != 'Tuesday': | ||
| 200 | raise DateTestError, 'weekday() failure' | ||
| 201 | if max(a,b) is not b or min(a,b) is not a: | ||
| 202 | raise DateTestError, 'min/max failure' | ||
| 203 | d = {a-1:b, b:a+1} | ||
| 204 | if d[b-366] != b or d[a+(b-a)] != Date(10,1,1913): | ||
| 205 | raise DateTestError, 'dictionary failure' | ||
| 206 | |||
| 207 | # verify date<->number conversions for first and last days for | ||
| 208 | # all years in firstyear .. lastyear | ||
| 209 | |||
| 210 | lord = _days_before_year(firstyear) | ||
| 211 | y = firstyear | ||
| 212 | while y <= lastyear: | ||
| 213 | ford = lord + 1 | ||
| 214 | lord = ford + _days_in_year(y) - 1 | ||
| 215 | fd, ld = Date(1,1,y), Date(12,31,y) | ||
| 216 | if (fd.ord,ld.ord) != (ford,lord): | ||
| 217 | raise DateTestError, ('date->num failed', y) | ||
| 218 | fd, ld = _num2date(ford), _num2date(lord) | ||
| 219 | if (1,1,y,12,31,y) != \ | ||
| 220 | (fd.month,fd.day,fd.year,ld.month,ld.day,ld.year): | ||
| 221 | raise DateTestError, ('num->date failed', y) | ||
| 222 | y = y + 1 | ||
| @@ -0,0 +1,66 @@ | |||
| 1 | # A wrapper around the (optional) built-in class dbm, supporting keys | ||
| 2 | # and values of almost any type instead of just string. | ||
| 3 | # (Actually, this works only for keys and values that can be read back | ||
| 4 | # correctly after being converted to a string.) | ||
| 5 | |||
| 6 | |||
| 7 | class Dbm: | ||
| 8 | |||
| 9 | def __init__(self, filename, mode, perm): | ||
| 10 | import dbm | ||
| 11 | self.db = dbm.open(filename, mode, perm) | ||
| 12 | |||
| 13 | def __repr__(self): | ||
| 14 | s = '' | ||
| 15 | for key in self.keys(): | ||
| 16 | t = repr(key) + ': ' + repr(self[key]) | ||
| 17 | if s: t = ', ' + t | ||
| 18 | s = s + t | ||
| 19 | return '{' + s + '}' | ||
| 20 | |||
| 21 | def __len__(self): | ||
| 22 | return len(self.db) | ||
| 23 | |||
| 24 | def __getitem__(self, key): | ||
| 25 | return eval(self.db[repr(key)]) | ||
| 26 | |||
| 27 | def __setitem__(self, key, value): | ||
| 28 | self.db[repr(key)] = repr(value) | ||
| 29 | |||
| 30 | def __delitem__(self, key): | ||
| 31 | del self.db[repr(key)] | ||
| 32 | |||
| 33 | def keys(self): | ||
| 34 | res = [] | ||
| 35 | for key in self.db.keys(): | ||
| 36 | res.append(eval(key)) | ||
| 37 | return res | ||
| 38 | |||
| 39 | def has_key(self, key): | ||
| 40 | return self.db.has_key(repr(key)) | ||
| 41 | |||
| 42 | |||
| 43 | def test(): | ||
| 44 | d = Dbm('@dbm', 'rw', 0600) | ||
| 45 | print d | ||
| 46 | while 1: | ||
| 47 | try: | ||
| 48 | key = input('key: ') | ||
| 49 | if d.has_key(key): | ||
| 50 | value = d[key] | ||
| 51 | print 'currently:', value | ||
| 52 | value = input('value: ') | ||
| 53 | if value == None: | ||
| 54 | del d[key] | ||
| 55 | else: | ||
| 56 | d[key] = value | ||
| 57 | except KeyboardInterrupt: | ||
| 58 | print '' | ||
| 59 | print d | ||
| 60 | except EOFError: | ||
| 61 | print '[eof]' | ||
| 62 | break | ||
| 63 | print d | ||
| 64 | |||
| 65 | |||
| 66 | test() | ||
| @@ -0,0 +1,13 @@ | |||
| 1 | Examples of classes that implement special operators (see reference manual): | ||
| 2 | |||
| 3 | Complex.py Complex numbers | ||
| 4 | Dates.py Date manipulation package by Tim Peters | ||
| 5 | Dbm.py Wrapper around built-in dbm, supporting arbitrary values | ||
| 6 | Range.py Example of a generator: re-implement built-in range() | ||
| 7 | Rat.py Rational numbers | ||
| 8 | Rev.py Yield the reverse of a sequence | ||
| 9 | Vec.py A simple vector class | ||
| 10 | bitvec.py A bit-vector class by Jan-Hein B\"uhrman | ||
| 11 | |||
| 12 | (For straightforward examples of basic class features, such as use of | ||
| 13 | methods and inheritance, see the library code.) | ||
| @@ -0,0 +1,93 @@ | |||
| 1 | """Example of a generator: re-implement the built-in range function | ||
| 2 | without actually constructing the list of values. | ||
| 3 | |||
| 4 | OldStyleRange is coded in the way required to work in a 'for' loop before | ||
| 5 | iterators were introduced into the language; using __getitem__ and __len__ . | ||
| 6 | |||
| 7 | """ | ||
| 8 | def handleargs(arglist): | ||
| 9 | """Take list of arguments and extract/create proper start, stop, and step | ||
| 10 | values and return in a tuple""" | ||
| 11 | try: | ||
| 12 | if len(arglist) == 1: | ||
| 13 | return 0, int(arglist[0]), 1 | ||
| 14 | elif len(arglist) == 2: | ||
| 15 | return int(arglist[0]), int(arglist[1]), 1 | ||
| 16 | elif len(arglist) == 3: | ||
| 17 | if arglist[2] == 0: | ||
| 18 | raise ValueError("step argument must not be zero") | ||
| 19 | return tuple(int(x) for x in arglist) | ||
| 20 | else: | ||
| 21 | raise TypeError("range() accepts 1-3 arguments, given", len(arglist)) | ||
| 22 | except TypeError: | ||
| 23 | raise TypeError("range() arguments must be numbers or strings " | ||
| 24 | "representing numbers") | ||
| 25 | |||
| 26 | def genrange(*a): | ||
| 27 | """Function to implement 'range' as a generator""" | ||
| 28 | start, stop, step = handleargs(a) | ||
| 29 | value = start | ||
| 30 | while value < stop: | ||
| 31 | yield value | ||
| 32 | value += step | ||
| 33 | |||
| 34 | class oldrange: | ||
| 35 | """Class implementing a range object. | ||
| 36 | To the user the instances feel like immutable sequences | ||
| 37 | (and you can't concatenate or slice them) | ||
| 38 | |||
| 39 | Done using the old way (pre-iterators; __len__ and __getitem__) to have an | ||
| 40 | object be used by a 'for' loop. | ||
| 41 | |||
| 42 | """ | ||
| 43 | |||
| 44 | def __init__(self, *a): | ||
| 45 | """ Initialize start, stop, and step values along with calculating the | ||
| 46 | nubmer of values (what __len__ will return) in the range""" | ||
| 47 | self.start, self.stop, self.step = handleargs(a) | ||
| 48 | self.len = max(0, (self.stop - self.start) // self.step) | ||
| 49 | |||
| 50 | def __repr__(self): | ||
| 51 | """implement repr(x) which is also used by print""" | ||
| 52 | return 'range(%r, %r, %r)' % (self.start, self.stop, self.step) | ||
| 53 | |||
| 54 | def __len__(self): | ||
| 55 | """implement len(x)""" | ||
| 56 | return self.len | ||
| 57 | |||
| 58 | def __getitem__(self, i): | ||
| 59 | """implement x[i]""" | ||
| 60 | if 0 <= i <= self.len: | ||
| 61 | return self.start + self.step * i | ||
| 62 | else: | ||
| 63 | raise IndexError, 'range[i] index out of range' | ||
| 64 | |||
| 65 | |||
| 66 | def test(): | ||
| 67 | import time, __builtin__ | ||
| 68 | #Just a quick sanity check | ||
| 69 | correct_result = __builtin__.range(5, 100, 3) | ||
| 70 | oldrange_result = list(oldrange(5, 100, 3)) | ||
| 71 | genrange_result = list(genrange(5, 100, 3)) | ||
| 72 | if genrange_result != correct_result or oldrange_result != correct_result: | ||
| 73 | raise Exception("error in implementation:\ncorrect = %s" | ||
| 74 | "\nold-style = %s\ngenerator = %s" % | ||
| 75 | (correct_result, oldrange_result, genrange_result)) | ||
| 76 | print "Timings for range(1000):" | ||
| 77 | t1 = time.time() | ||
| 78 | for i in oldrange(1000): | ||
| 79 | pass | ||
| 80 | t2 = time.time() | ||
| 81 | for i in genrange(1000): | ||
| 82 | pass | ||
| 83 | t3 = time.time() | ||
| 84 | for i in __builtin__.range(1000): | ||
| 85 | pass | ||
| 86 | t4 = time.time() | ||
| 87 | print t2-t1, 'sec (old-style class)' | ||
| 88 | print t3-t2, 'sec (generator)' | ||
| 89 | print t4-t3, 'sec (built-in)' | ||
| 90 | |||
| 91 | |||
| 92 | if __name__ == '__main__': | ||
| 93 | test() | ||
| @@ -0,0 +1,310 @@ | |||
| 1 | '''\ | ||
| 2 | This module implements rational numbers. | ||
| 3 | |||
| 4 | The entry point of this module is the function | ||
| 5 | rat(numerator, denominator) | ||
| 6 | If either numerator or denominator is of an integral or rational type, | ||
| 7 | the result is a rational number, else, the result is the simplest of | ||
| 8 | the types float and complex which can hold numerator/denominator. | ||
| 9 | If denominator is omitted, it defaults to 1. | ||
| 10 | Rational numbers can be used in calculations with any other numeric | ||
| 11 | type. The result of the calculation will be rational if possible. | ||
| 12 | |||
| 13 | There is also a test function with calling sequence | ||
| 14 | test() | ||
| 15 | The documentation string of the test function contains the expected | ||
| 16 | output. | ||
| 17 | ''' | ||
| 18 | |||
| 19 | # Contributed by Sjoerd Mullender | ||
| 20 | |||
| 21 | from types import * | ||
| 22 | |||
| 23 | def gcd(a, b): | ||
| 24 | '''Calculate the Greatest Common Divisor.''' | ||
| 25 | while b: | ||
| 26 | a, b = b, a%b | ||
| 27 | return a | ||
| 28 | |||
| 29 | def rat(num, den = 1): | ||
| 30 | # must check complex before float | ||
| 31 | if isinstance(num, complex) or isinstance(den, complex): | ||
| 32 | # numerator or denominator is complex: return a complex | ||
| 33 | return complex(num) / complex(den) | ||
| 34 | if isinstance(num, float) or isinstance(den, float): | ||
| 35 | # numerator or denominator is float: return a float | ||
| 36 | return float(num) / float(den) | ||
| 37 | # otherwise return a rational | ||
| 38 | return Rat(num, den) | ||
| 39 | |||
| 40 | class Rat: | ||
| 41 | '''This class implements rational numbers.''' | ||
| 42 | |||
| 43 | def __init__(self, num, den = 1): | ||
| 44 | if den == 0: | ||
| 45 | raise ZeroDivisionError, 'rat(x, 0)' | ||
| 46 | |||
| 47 | # normalize | ||
| 48 | |||
| 49 | # must check complex before float | ||
| 50 | if (isinstance(num, complex) or | ||
| 51 | isinstance(den, complex)): | ||
| 52 | # numerator or denominator is complex: | ||
| 53 | # normalized form has denominator == 1+0j | ||
| 54 | self.__num = complex(num) / complex(den) | ||
| 55 | self.__den = complex(1) | ||
| 56 | return | ||
| 57 | if isinstance(num, float) or isinstance(den, float): | ||
| 58 | # numerator or denominator is float: | ||
| 59 | # normalized form has denominator == 1.0 | ||
| 60 | self.__num = float(num) / float(den) | ||
| 61 | self.__den = 1.0 | ||
| 62 | return | ||
| 63 | if (isinstance(num, self.__class__) or | ||
| 64 | isinstance(den, self.__class__)): | ||
| 65 | # numerator or denominator is rational | ||
| 66 | new = num / den | ||
| 67 | if not isinstance(new, self.__class__): | ||
| 68 | self.__num = new | ||
| 69 | if isinstance(new, complex): | ||
| 70 | self.__den = complex(1) | ||
| 71 | else: | ||
| 72 | self.__den = 1.0 | ||
| 73 | else: | ||
| 74 | self.__num = new.__num | ||
| 75 | self.__den = new.__den | ||
| 76 | else: | ||
| 77 | # make sure numerator and denominator don't | ||
| 78 | # have common factors | ||
| 79 | # this also makes sure that denominator > 0 | ||
| 80 | g = gcd(num, den) | ||
| 81 | self.__num = num / g | ||
| 82 | self.__den = den / g | ||
| 83 | # try making numerator and denominator of IntType if they fit | ||
| 84 | try: | ||
| 85 | numi = int(self.__num) | ||
| 86 | deni = int(self.__den) | ||
| 87 | except (OverflowError, TypeError): | ||
| 88 | pass | ||
| 89 | else: | ||
| 90 | if self.__num == numi and self.__den == deni: | ||
| 91 | self.__num = numi | ||
| 92 | self.__den = deni | ||
| 93 | |||
| 94 | def __repr__(self): | ||
| 95 | return 'Rat(%s,%s)' % (self.__num, self.__den) | ||
| 96 | |||
| 97 | def __str__(self): | ||
| 98 | if self.__den == 1: | ||
| 99 | return str(self.__num) | ||
| 100 | else: | ||
| 101 | return '(%s/%s)' % (str(self.__num), str(self.__den)) | ||
| 102 | |||
| 103 | # a + b | ||
| 104 | def __add__(a, b): | ||
| 105 | try: | ||
| 106 | return rat(a.__num * b.__den + b.__num * a.__den, | ||
| 107 | a.__den * b.__den) | ||
| 108 | except OverflowError: | ||
| 109 | return rat(long(a.__num) * long(b.__den) + | ||
| 110 | long(b.__num) * long(a.__den), | ||
| 111 | long(a.__den) * long(b.__den)) | ||
| 112 | |||
| 113 | def __radd__(b, a): | ||
| 114 | return Rat(a) + b | ||
| 115 | |||
| 116 | # a - b | ||
| 117 | def __sub__(a, b): | ||
| 118 | try: | ||
| 119 | return rat(a.__num * b.__den - b.__num * a.__den, | ||
| 120 | a.__den * b.__den) | ||
| 121 | except OverflowError: | ||
| 122 | return rat(long(a.__num) * long(b.__den) - | ||
| 123 | long(b.__num) * long(a.__den), | ||
| 124 | long(a.__den) * long(b.__den)) | ||
| 125 | |||
| 126 | def __rsub__(b, a): | ||
| 127 | return Rat(a) - b | ||
| 128 | |||
| 129 | # a * b | ||
| 130 | def __mul__(a, b): | ||
| 131 | try: | ||
| 132 | return rat(a.__num * b.__num, a.__den * b.__den) | ||
| 133 | except OverflowError: | ||
| 134 | return rat(long(a.__num) * long(b.__num), | ||
| 135 | long(a.__den) * long(b.__den)) | ||
| 136 | |||
| 137 | def __rmul__(b, a): | ||
| 138 | return Rat(a) * b | ||
| 139 | |||
| 140 | # a / b | ||
| 141 | def __div__(a, b): | ||
| 142 | try: | ||
| 143 | return rat(a.__num * b.__den, a.__den * b.__num) | ||
| 144 | except OverflowError: | ||
| 145 | return rat(long(a.__num) * long(b.__den), | ||
| 146 | long(a.__den) * long(b.__num)) | ||
| 147 | |||
| 148 | def __rdiv__(b, a): | ||
| 149 | return Rat(a) / b | ||
| 150 | |||
| 151 | # a % b | ||
| 152 | def __mod__(a, b): | ||
| 153 | div = a / b | ||
| 154 | try: | ||
| 155 | div = int(div) | ||
| 156 | except OverflowError: | ||
| 157 | div = long(div) | ||
| 158 | return a - b * div | ||
| 159 | |||
| 160 | def __rmod__(b, a): | ||
| 161 | return Rat(a) % b | ||
| 162 | |||
| 163 | # a ** b | ||
| 164 | def __pow__(a, b): | ||
| 165 | if b.__den != 1: | ||
| 166 | if isinstance(a.__num, complex): | ||
| 167 | a = complex(a) | ||
| 168 | else: | ||
| 169 | a = float(a) | ||
| 170 | if isinstance(b.__num, complex): | ||
| 171 | b = complex(b) | ||
| 172 | else: | ||
| 173 | b = float(b) | ||
| 174 | return a ** b | ||
| 175 | try: | ||
| 176 | return rat(a.__num ** b.__num, a.__den ** b.__num) | ||
| 177 | except OverflowError: | ||
| 178 | return rat(long(a.__num) ** b.__num, | ||
| 179 | long(a.__den) ** b.__num) | ||
| 180 | |||
| 181 | def __rpow__(b, a): | ||
| 182 | return Rat(a) ** b | ||
| 183 | |||
| 184 | # -a | ||
| 185 | def __neg__(a): | ||
| 186 | try: | ||
| 187 | return rat(-a.__num, a.__den) | ||
| 188 | except OverflowError: | ||
| 189 | # a.__num == sys.maxint | ||
| 190 | return rat(-long(a.__num), a.__den) | ||
| 191 | |||
| 192 | # abs(a) | ||
| 193 | def __abs__(a): | ||
| 194 | return rat(abs(a.__num), a.__den) | ||
| 195 | |||
| 196 | # int(a) | ||
| 197 | def __int__(a): | ||
| 198 | return int(a.__num / a.__den) | ||
| 199 | |||
| 200 | # long(a) | ||
| 201 | def __long__(a): | ||
| 202 | return long(a.__num) / long(a.__den) | ||
| 203 | |||
| 204 | # float(a) | ||
| 205 | def __float__(a): | ||
| 206 | return float(a.__num) / float(a.__den) | ||
| 207 | |||
| 208 | # complex(a) | ||
| 209 | def __complex__(a): | ||
| 210 | return complex(a.__num) / complex(a.__den) | ||
| 211 | |||
| 212 | # cmp(a,b) | ||
| 213 | def __cmp__(a, b): | ||
| 214 | diff = Rat(a - b) | ||
| 215 | if diff.__num < 0: | ||
| 216 | return -1 | ||
| 217 | elif diff.__num > 0: | ||
| 218 | return 1 | ||
| 219 | else: | ||
| 220 | return 0 | ||
| 221 | |||
| 222 | def __rcmp__(b, a): | ||
| 223 | return cmp(Rat(a), b) | ||
| 224 | |||
| 225 | # a != 0 | ||
| 226 | def __nonzero__(a): | ||
| 227 | return a.__num != 0 | ||
| 228 | |||
| 229 | # coercion | ||
| 230 | def __coerce__(a, b): | ||
| 231 | return a, Rat(b) | ||
| 232 | |||
| 233 | def test(): | ||
| 234 | '''\ | ||
| 235 | Test function for rat module. | ||
| 236 | |||
| 237 | The expected output is (module some differences in floating | ||
| 238 | precission): | ||
| 239 | -1 | ||
| 240 | -1 | ||
| 241 | 0 0L 0.1 (0.1+0j) | ||
| 242 | [Rat(1,2), Rat(-3,10), Rat(1,25), Rat(1,4)] | ||
| 243 | [Rat(-3,10), Rat(1,25), Rat(1,4), Rat(1,2)] | ||
| 244 | 0 | ||
| 245 | (11/10) | ||
| 246 | (11/10) | ||
| 247 | 1.1 | ||
| 248 | OK | ||
| 249 | 2 1.5 (3/2) (1.5+1.5j) (15707963/5000000) | ||
| 250 | 2 2 2.0 (2+0j) | ||
| 251 | |||
| 252 | 4 0 4 1 4 0 | ||
| 253 | 3.5 0.5 3.0 1.33333333333 2.82842712475 1 | ||
| 254 | (7/2) (1/2) 3 (4/3) 2.82842712475 1 | ||
| 255 | (3.5+1.5j) (0.5-1.5j) (3+3j) (0.666666666667-0.666666666667j) (1.43248815986+2.43884761145j) 1 | ||
| 256 | 1.5 1 1.5 (1.5+0j) | ||
| 257 | |||
| 258 | 3.5 -0.5 3.0 0.75 2.25 -1 | ||
| 259 | 3.0 0.0 2.25 1.0 1.83711730709 0 | ||
| 260 | 3.0 0.0 2.25 1.0 1.83711730709 1 | ||
| 261 | (3+1.5j) -1.5j (2.25+2.25j) (0.5-0.5j) (1.50768393746+1.04970907623j) -1 | ||
| 262 | (3/2) 1 1.5 (1.5+0j) | ||
| 263 | |||
| 264 | (7/2) (-1/2) 3 (3/4) (9/4) -1 | ||
| 265 | 3.0 0.0 2.25 1.0 1.83711730709 -1 | ||
| 266 | 3 0 (9/4) 1 1.83711730709 0 | ||
| 267 | (3+1.5j) -1.5j (2.25+2.25j) (0.5-0.5j) (1.50768393746+1.04970907623j) -1 | ||
| 268 | (1.5+1.5j) (1.5+1.5j) | ||
| 269 | |||
| 270 | (3.5+1.5j) (-0.5+1.5j) (3+3j) (0.75+0.75j) 4.5j -1 | ||
| 271 | (3+1.5j) 1.5j (2.25+2.25j) (1+1j) (1.18235814075+2.85446505899j) 1 | ||
| 272 | (3+1.5j) 1.5j (2.25+2.25j) (1+1j) (1.18235814075+2.85446505899j) 1 | ||
| 273 | (3+3j) 0j 4.5j (1+0j) (-0.638110484918+0.705394566962j) 0 | ||
| 274 | ''' | ||
| 275 | print rat(-1L, 1) | ||
| 276 | print rat(1, -1) | ||
| 277 | a = rat(1, 10) | ||
| 278 | print int(a), long(a), float(a), complex(a) | ||
| 279 | b = rat(2, 5) | ||
| 280 | l = [a+b, a-b, a*b, a/b] | ||
| 281 | print l | ||
| 282 | l.sort() | ||
| 283 | print l | ||
| 284 | print rat(0, 1) | ||
| 285 | print a+1 | ||
| 286 | print a+1L | ||
| 287 | print a+1.0 | ||
| 288 | try: | ||
| 289 | print rat(1, 0) | ||
| 290 | raise SystemError, 'should have been ZeroDivisionError' | ||
| 291 | except ZeroDivisionError: | ||
| 292 | print 'OK' | ||
| 293 | print rat(2), rat(1.5), rat(3, 2), rat(1.5+1.5j), rat(31415926,10000000) | ||
| 294 | list = [2, 1.5, rat(3,2), 1.5+1.5j] | ||
| 295 | for i in list: | ||
| 296 | print i, | ||
| 297 | if not isinstance(i, complex): | ||
| 298 | print int(i), float(i), | ||
| 299 | print complex(i) | ||
| 300 | |||
| 301 | for j in list: | ||
| 302 | print i + j, i - j, i * j, i / j, i ** j, | ||
| 303 | if not (isinstance(i, complex) or | ||
| 304 | isinstance(j, complex)): | ||
| 305 | print cmp(i, j) | ||
| 306 | |||
| 307 | |||
| 308 | |||
| 309 | if __name__ == '__main__': | ||
| 310 | test() | ||
| @@ -0,0 +1,95 @@ | |||
| 1 | ''' | ||
| 2 | A class which presents the reverse of a sequence without duplicating it. | ||
| 3 | From: "Steven D. Majewski" <sdm7g@elvis.med.virginia.edu> | ||
| 4 | |||
| 5 | It works on mutable or inmutable sequences. | ||
| 6 | |||
| 7 | >>> chars = list(Rev('Hello World!')) | ||
| 8 | >>> print ''.join(chars) | ||
| 9 | !dlroW olleH | ||
| 10 | |||
| 11 | The .forw is so you can use anonymous sequences in __init__, and still | ||
| 12 | keep a reference the forward sequence. ) | ||
| 13 | If you give it a non-anonymous mutable sequence, the reverse sequence | ||
| 14 | will track the updated values. ( but not reassignment! - another | ||
| 15 | good reason to use anonymous values in creating the sequence to avoid | ||
| 16 | confusion. Maybe it should be change to copy input sequence to break | ||
| 17 | the connection completely ? ) | ||
| 18 | |||
| 19 | >>> nnn = range(3) | ||
| 20 | >>> rnn = Rev(nnn) | ||
| 21 | >>> for n in rnn: print n | ||
| 22 | ... | ||
| 23 | 2 | ||
| 24 | 1 | ||
| 25 | 0 | ||
| 26 | >>> for n in range(4, 6): nnn.append(n) # update nnn | ||
| 27 | ... | ||
| 28 | >>> for n in rnn: print n # prints reversed updated values | ||
| 29 | ... | ||
| 30 | 5 | ||
| 31 | 4 | ||
| 32 | 2 | ||
| 33 | 1 | ||
| 34 | 0 | ||
| 35 | >>> nnn = nnn[1:-1] | ||
| 36 | >>> nnn | ||
| 37 | [1, 2, 4] | ||
| 38 | >>> for n in rnn: print n # prints reversed values of old nnn | ||
| 39 | ... | ||
| 40 | 5 | ||
| 41 | 4 | ||
| 42 | 2 | ||
| 43 | 1 | ||
| 44 | 0 | ||
| 45 | |||
| 46 | # | ||
| 47 | >>> WH = Rev('Hello World!') | ||
| 48 | >>> print WH.forw, WH.back | ||
| 49 | Hello World! !dlroW olleH | ||
| 50 | >>> nnn = Rev(range(1, 10)) | ||
| 51 | >>> print nnn.forw | ||
| 52 | [1, 2, 3, 4, 5, 6, 7, 8, 9] | ||
| 53 | >>> print nnn.back | ||
| 54 | [9, 8, 7, 6, 5, 4, 3, 2, 1] | ||
| 55 | |||
| 56 | >>> rrr = Rev(nnn) | ||
| 57 | >>> rrr | ||
| 58 | <1, 2, 3, 4, 5, 6, 7, 8, 9> | ||
| 59 | |||
| 60 | ''' | ||
| 61 | |||
| 62 | class Rev: | ||
| 63 | def __init__(self, seq): | ||
| 64 | self.forw = seq | ||
| 65 | self.back = self | ||
| 66 | |||
| 67 | def __len__(self): | ||
| 68 | return len(self.forw) | ||
| 69 | |||
| 70 | def __getitem__(self, j): | ||
| 71 | return self.forw[-(j + 1)] | ||
| 72 | |||
| 73 | def __repr__(self): | ||
| 74 | seq = self.forw | ||
| 75 | if isinstance(seq, list): | ||
| 76 | wrap = '[]' | ||
| 77 | sep = ', ' | ||
| 78 | elif isinstance(seq, tuple): | ||
| 79 | wrap = '()' | ||
| 80 | sep = ', ' | ||
| 81 | elif isinstance(seq, str): | ||
| 82 | wrap = '' | ||
| 83 | sep = '' | ||
| 84 | else: | ||
| 85 | wrap = '<>' | ||
| 86 | sep = ', ' | ||
| 87 | outstrs = [str(item) for item in self.back] | ||
| 88 | return wrap[:1] + sep.join(outstrs) + wrap[-1:] | ||
| 89 | |||
| 90 | def _test(): | ||
| 91 | import doctest, Rev | ||
| 92 | return doctest.testmod(Rev) | ||
| 93 | |||
| 94 | if __name__ == "__main__": | ||
| 95 | _test() | ||
| @@ -0,0 +1,54 @@ | |||
| 1 | # A simple vector class | ||
| 2 | |||
| 3 | |||
| 4 | def vec(*v): | ||
| 5 | return Vec(*v) | ||
| 6 | |||
| 7 | |||
| 8 | class Vec: | ||
| 9 | |||
| 10 | def __init__(self, *v): | ||
| 11 | self.v = list(v) | ||
| 12 | |||
| 13 | def fromlist(self, v): | ||
| 14 | if not isinstance(v, list): | ||
| 15 | raise TypeError | ||
| 16 | self.v = v[:] | ||
| 17 | return self | ||
| 18 | |||
| 19 | def __repr__(self): | ||
| 20 | return 'vec(' + repr(self.v)[1:-1] + ')' | ||
| 21 | |||
| 22 | def __len__(self): | ||
| 23 | return len(self.v) | ||
| 24 | |||
| 25 | def __getitem__(self, i): | ||
| 26 | return self.v[i] | ||
| 27 | |||
| 28 | def __add__(self, other): | ||
| 29 | # Element-wise addition | ||
| 30 | v = map(lambda x, y: x+y, self, other) | ||
| 31 | return Vec().fromlist(v) | ||
| 32 | |||
| 33 | def __sub__(self, other): | ||
| 34 | # Element-wise subtraction | ||
| 35 | v = map(lambda x, y: x-y, self, other) | ||
| 36 | return Vec().fromlist(v) | ||
| 37 | |||
| 38 | def __mul__(self, scalar): | ||
| 39 | # Multiply by scalar | ||
| 40 | v = map(lambda x: x*scalar, self.v) | ||
| 41 | return Vec().fromlist(v) | ||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | def test(): | ||
| 46 | a = vec(1, 2, 3) | ||
| 47 | b = vec(3, 2, 1) | ||
| 48 | print a | ||
| 49 | print b | ||
| 50 | print a+b | ||
| 51 | print a-b | ||
| 52 | print a*3.0 | ||
| 53 | |||
| 54 | test() | ||
| @@ -0,0 +1,332 @@ | |||
| 1 | # | ||
| 2 | # this is a rather strict implementation of a bit vector class | ||
| 3 | # it is accessed the same way as an array of python-ints, except | ||
| 4 | # the value must be 0 or 1 | ||
| 5 | # | ||
| 6 | |||
| 7 | import sys; rprt = sys.stderr.write #for debugging | ||
| 8 | |||
| 9 | error = 'bitvec.error' | ||
| 10 | |||
| 11 | |||
| 12 | def _check_value(value): | ||
| 13 | if type(value) != type(0) or not 0 <= value < 2: | ||
| 14 | raise error, 'bitvec() items must have int value 0 or 1' | ||
| 15 | |||
| 16 | |||
| 17 | import math | ||
| 18 | |||
| 19 | def _compute_len(param): | ||
| 20 | mant, l = math.frexp(float(param)) | ||
| 21 | bitmask = 1L << l | ||
| 22 | if bitmask <= param: | ||
| 23 | raise 'FATAL', '(param, l) = %r' % ((param, l),) | ||
| 24 | while l: | ||
| 25 | bitmask = bitmask >> 1 | ||
| 26 | if param & bitmask: | ||
| 27 | break | ||
| 28 | l = l - 1 | ||
| 29 | return l | ||
| 30 | |||
| 31 | |||
| 32 | def _check_key(len, key): | ||
| 33 | if type(key) != type(0): | ||
| 34 | raise TypeError, 'sequence subscript not int' | ||
| 35 | if key < 0: | ||
| 36 | key = key + len | ||
| 37 | if not 0 <= key < len: | ||
| 38 | raise IndexError, 'list index out of range' | ||
| 39 | return key | ||
| 40 | |||
| 41 | def _check_slice(len, i, j): | ||
| 42 | #the type is ok, Python already checked that | ||
| 43 | i, j = max(i, 0), min(len, j) | ||
| 44 | if i > j: | ||
| 45 | i = j | ||
| 46 | return i, j | ||
| 47 | |||
| 48 | |||
| 49 | class BitVec: | ||
| 50 | |||
| 51 | def __init__(self, *params): | ||
| 52 | self._data = 0L | ||
| 53 | self._len = 0 | ||
| 54 | if not len(params): | ||
| 55 | pass | ||
| 56 | elif len(params) == 1: | ||
| 57 | param, = params | ||
| 58 | if type(param) == type([]): | ||
| 59 | value = 0L | ||
| 60 | bit_mask = 1L | ||
| 61 | for item in param: | ||
| 62 | # strict check | ||
| 63 | #_check_value(item) | ||
| 64 | if item: | ||
| 65 | value = value | bit_mask | ||
| 66 | bit_mask = bit_mask << 1 | ||
| 67 | self._data = value | ||
| 68 | self._len = len(param) | ||
| 69 | elif type(param) == type(0L): | ||
| 70 | if param < 0: | ||
| 71 | raise error, 'bitvec() can\'t handle negative longs' | ||
| 72 | self._data = param | ||
| 73 | self._len = _compute_len(param) | ||
| 74 | else: | ||
| 75 | raise error, 'bitvec() requires array or long parameter' | ||
| 76 | elif len(params) == 2: | ||
| 77 | param, length = params | ||
| 78 | if type(param) == type(0L): | ||
| 79 | if param < 0: | ||
| 80 | raise error, \ | ||
| 81 | 'can\'t handle negative longs' | ||
| 82 | self._data = param | ||
| 83 | if type(length) != type(0): | ||
| 84 | raise error, 'bitvec()\'s 2nd parameter must be int' | ||
| 85 | computed_length = _compute_len(param) | ||
| 86 | if computed_length > length: | ||
| 87 | print 'warning: bitvec() value is longer than the length indicates, truncating value' | ||
| 88 | self._data = self._data & \ | ||
| 89 | ((1L << length) - 1) | ||
| 90 | self._len = length | ||
| 91 | else: | ||
| 92 | raise error, 'bitvec() requires array or long parameter' | ||
| 93 | else: | ||
| 94 | raise error, 'bitvec() requires 0 -- 2 parameter(s)' | ||
| 95 | |||
| 96 | |||
| 97 | def append(self, item): | ||
| 98 | #_check_value(item) | ||
| 99 | #self[self._len:self._len] = [item] | ||
| 100 | self[self._len:self._len] = \ | ||
| 101 | BitVec(long(not not item), 1) | ||
| 102 | |||
| 103 | |||
| 104 | def count(self, value): | ||
| 105 | #_check_value(value) | ||
| 106 | if value: | ||
| 107 | data = self._data | ||
| 108 | else: | ||
| 109 | data = (~self)._data | ||
| 110 | count = 0 | ||
| 111 | while data: | ||
| 112 | data, count = data >> 1, count + (data & 1 != 0) | ||
| 113 | return count | ||
| 114 | |||
| 115 | |||
| 116 | def index(self, value): | ||
| 117 | #_check_value(value): | ||
| 118 | if value: | ||
| 119 | data = self._data | ||
| 120 | else: | ||
| 121 | data = (~self)._data | ||
| 122 | index = 0 | ||
| 123 | if not data: | ||
| 124 | raise ValueError, 'list.index(x): x not in list' | ||
| 125 | while not (data & 1): | ||
| 126 | data, index = data >> 1, index + 1 | ||
| 127 | return index | ||
| 128 | |||
| 129 | |||
| 130 | def insert(self, index, item): | ||
| 131 | #_check_value(item) | ||
| 132 | #self[index:index] = [item] | ||
| 133 | self[index:index] = BitVec(long(not not item), 1) | ||
| 134 | |||
| 135 | |||
| 136 | def remove(self, value): | ||
| 137 | del self[self.index(value)] | ||
| 138 | |||
| 139 | |||
| 140 | def reverse(self): | ||
| 141 | #ouch, this one is expensive! | ||
| 142 | #for i in self._len>>1: self[i], self[l-i] = self[l-i], self[i] | ||
| 143 | data, result = self._data, 0L | ||
| 144 | for i in range(self._len): | ||
| 145 | if not data: | ||
| 146 | result = result << (self._len - i) | ||
| 147 | break | ||
| 148 | result, data = (result << 1) | (data & 1), data >> 1 | ||
| 149 | self._data = result | ||
| 150 | |||
| 151 | |||
| 152 | def sort(self): | ||
| 153 | c = self.count(1) | ||
| 154 | self._data = ((1L << c) - 1) << (self._len - c) | ||
| 155 | |||
| 156 | |||
| 157 | def copy(self): | ||
| 158 | return BitVec(self._data, self._len) | ||
| 159 | |||
| 160 | |||
| 161 | def seq(self): | ||
| 162 | result = [] | ||
| 163 | for i in self: | ||
| 164 | result.append(i) | ||
| 165 | return result | ||
| 166 | |||
| 167 | |||
| 168 | def __repr__(self): | ||
| 169 | ##rprt('<bitvec class instance object>.' + '__repr__()\n') | ||
| 170 | return 'bitvec(%r, %r)' % (self._data, self._len) | ||
| 171 | |||
| 172 | def __cmp__(self, other, *rest): | ||
| 173 | #rprt('%r.__cmp__%r\n' % (self, (other,) + rest)) | ||
| 174 | if type(other) != type(self): | ||
| 175 | other = apply(bitvec, (other, ) + rest) | ||
| 176 | #expensive solution... recursive binary, with slicing | ||
| 177 | length = self._len | ||
| 178 | if length == 0 or other._len == 0: | ||
| 179 | return cmp(length, other._len) | ||
| 180 | if length != other._len: | ||
| 181 | min_length = min(length, other._len) | ||
| 182 | return cmp(self[:min_length], other[:min_length]) or \ | ||
| 183 | cmp(self[min_length:], other[min_length:]) | ||
| 184 | #the lengths are the same now... | ||
| 185 | if self._data == other._data: | ||
| 186 | return 0 | ||
| 187 | if length == 1: | ||
| 188 | return cmp(self[0], other[0]) | ||
| 189 | else: | ||
| 190 | length = length >> 1 | ||
| 191 | return cmp(self[:length], other[:length]) or \ | ||
| 192 | cmp(self[length:], other[length:]) | ||
| 193 | |||
| 194 | |||
| 195 | def __len__(self): | ||
| 196 | #rprt('%r.__len__()\n' % (self,)) | ||
| 197 | return self._len | ||
| 198 | |||
| 199 | def __getitem__(self, key): | ||
| 200 | #rprt('%r.__getitem__(%r)\n' % (self, key)) | ||
| 201 | key = _check_key(self._len, key) | ||
| 202 | return self._data & (1L << key) != 0 | ||
| 203 | |||
| 204 | def __setitem__(self, key, value): | ||
| 205 | #rprt('%r.__setitem__(%r, %r)\n' % (self, key, value)) | ||
| 206 | key = _check_key(self._len, key) | ||
| 207 | #_check_value(value) | ||
| 208 | if value: | ||
| 209 | self._data = self._data | (1L << key) | ||
| 210 | else: | ||
| 211 | self._data = self._data & ~(1L << key) | ||
| 212 | |||
| 213 | def __delitem__(self, key): | ||
| 214 | #rprt('%r.__delitem__(%r)\n' % (self, key)) | ||
| 215 | key = _check_key(self._len, key) | ||
| 216 | #el cheapo solution... | ||
| 217 | self._data = self[:key]._data | self[key+1:]._data >> key | ||
| 218 | self._len = self._len - 1 | ||
| 219 | |||
| 220 | def __getslice__(self, i, j): | ||
| 221 | #rprt('%r.__getslice__(%r, %r)\n' % (self, i, j)) | ||
| 222 | i, j = _check_slice(self._len, i, j) | ||
| 223 | if i >= j: | ||
| 224 | return BitVec(0L, 0) | ||
| 225 | if i: | ||
| 226 | ndata = self._data >> i | ||
| 227 | else: | ||
| 228 | ndata = self._data | ||
| 229 | nlength = j - i | ||
| 230 | if j != self._len: | ||
| 231 | #we'll have to invent faster variants here | ||
| 232 | #e.g. mod_2exp | ||
| 233 | ndata = ndata & ((1L << nlength) - 1) | ||
| 234 | return BitVec(ndata, nlength) | ||
| 235 | |||
| 236 | def __setslice__(self, i, j, sequence, *rest): | ||
| 237 | #rprt('%s.__setslice__%r\n' % (self, (i, j, sequence) + rest)) | ||
| 238 | i, j = _check_slice(self._len, i, j) | ||
| 239 | if type(sequence) != type(self): | ||
| 240 | sequence = apply(bitvec, (sequence, ) + rest) | ||
| 241 | #sequence is now of our own type | ||
| 242 | ls_part = self[:i] | ||
| 243 | ms_part = self[j:] | ||
| 244 | self._data = ls_part._data | \ | ||
| 245 | ((sequence._data | \ | ||
| 246 | (ms_part._data << sequence._len)) << ls_part._len) | ||
| 247 | self._len = self._len - j + i + sequence._len | ||
| 248 | |||
| 249 | def __delslice__(self, i, j): | ||
| 250 | #rprt('%r.__delslice__(%r, %r)\n' % (self, i, j)) | ||
| 251 | i, j = _check_slice(self._len, i, j) | ||
| 252 | if i == 0 and j == self._len: | ||
| 253 | self._data, self._len = 0L, 0 | ||
| 254 | elif i < j: | ||
| 255 | self._data = self[:i]._data | (self[j:]._data >> i) | ||
| 256 | self._len = self._len - j + i | ||
| 257 | |||
| 258 | def __add__(self, other): | ||
| 259 | #rprt('%r.__add__(%r)\n' % (self, other)) | ||
| 260 | retval = self.copy() | ||
| 261 | retval[self._len:self._len] = other | ||
| 262 | return retval | ||
| 263 | |||
| 264 | def __mul__(self, multiplier): | ||
| 265 | #rprt('%r.__mul__(%r)\n' % (self, multiplier)) | ||
| 266 | if type(multiplier) != type(0): | ||
| 267 | raise TypeError, 'sequence subscript not int' | ||
| 268 | if multiplier <= 0: | ||
| 269 | return BitVec(0L, 0) | ||
| 270 | elif multiplier == 1: | ||
| 271 | return self.copy() | ||
| 272 | #handle special cases all 0 or all 1... | ||
| 273 | if self._data == 0L: | ||
| 274 | return BitVec(0L, self._len * multiplier) | ||
| 275 | elif (~self)._data == 0L: | ||
| 276 | return ~BitVec(0L, self._len * multiplier) | ||
| 277 | #otherwise el cheapo again... | ||
| 278 | retval = BitVec(0L, 0) | ||
| 279 | while multiplier: | ||
| 280 | retval, multiplier = retval + self, multiplier - 1 | ||
| 281 | return retval | ||
| 282 | |||
| 283 | def __and__(self, otherseq, *rest): | ||
| 284 | #rprt('%r.__and__%r\n' % (self, (otherseq,) + rest)) | ||
| 285 | if type(otherseq) != type(self): | ||
| 286 | otherseq = apply(bitvec, (otherseq, ) + rest) | ||
| 287 | #sequence is now of our own type | ||
| 288 | return BitVec(self._data & otherseq._data, \ | ||
| 289 | min(self._len, otherseq._len)) | ||
| 290 | |||
| 291 | |||
| 292 | def __xor__(self, otherseq, *rest): | ||
| 293 | #rprt('%r.__xor__%r\n' % (self, (otherseq,) + rest)) | ||
| 294 | if type(otherseq) != type(self): | ||
| 295 | otherseq = apply(bitvec, (otherseq, ) + rest) | ||
| 296 | #sequence is now of our own type | ||
| 297 | return BitVec(self._data ^ otherseq._data, \ | ||
| 298 | max(self._len, otherseq._len)) | ||
| 299 | |||
| 300 | |||
| 301 | def __or__(self, otherseq, *rest): | ||
| 302 | #rprt('%r.__or__%r\n' % (self, (otherseq,) + rest)) | ||
| 303 | if type(otherseq) != type(self): | ||
| 304 | otherseq = apply(bitvec, (otherseq, ) + rest) | ||
| 305 | #sequence is now of our own type | ||
| 306 | return BitVec(self._data | otherseq._data, \ | ||
| 307 | max(self._len, otherseq._len)) | ||
| 308 | |||
| 309 | |||
| 310 | def __invert__(self): | ||
| 311 | #rprt('%r.__invert__()\n' % (self,)) | ||
| 312 | return BitVec(~self._data & ((1L << self._len) - 1), \ | ||
| 313 | self._len) | ||
| 314 | |||
| 315 | def __coerce__(self, otherseq, *rest): | ||
| 316 | #needed for *some* of the arithmetic operations | ||
| 317 | #rprt('%r.__coerce__%r\n' % (self, (otherseq,) + rest)) | ||
| 318 | if type(otherseq) != type(self): | ||
| 319 | otherseq = apply(bitvec, (otherseq, ) + rest) | ||
| 320 | return self, otherseq | ||
| 321 | |||
| 322 | def __int__(self): | ||
| 323 | return int(self._data) | ||
| 324 | |||
| 325 | def __long__(self): | ||
| 326 | return long(self._data) | ||
| 327 | |||
| 328 | def __float__(self): | ||
| 329 | return float(self._data) | ||
| 330 | |||
| 331 | |||
| 332 | bitvec = BitVec | ||
| @@ -0,0 +1,60 @@ | |||
| 1 | Subject: Re: What language would you use? | ||
| 2 | From: Tom Christiansen <tchrist@mox.perl.com> | ||
| 3 | Date: 6 Nov 1994 15:14:51 GMT | ||
| 4 | Newsgroups: comp.lang.python,comp.lang.tcl,comp.lang.scheme,comp.lang.misc,comp.lang.perl | ||
| 5 | Message-Id: <39irtb$3t4@csnews.cs.Colorado.EDU> | ||
| 6 | References: <39b7ha$j9v@zeno.nscf.org> <39hhjp$lgn@csnews.cs.Colorado.EDU> <39hvsu$dus@mathserv.mps.ohio-state.edu> | ||
| 7 | |||
| 8 | [...] | ||
| 9 | If you're really into benchmarks, I'd love it if someone were to code up | ||
| 10 | the following problems in tcl, python, and scheme (and whatever else you'd | ||
| 11 | like). Separate versions (one optimized for speed, one for beauty :-) are | ||
| 12 | ok. Post your code so we can time it on our own systems. | ||
| 13 | |||
| 14 | 0) Factorial Test (numerics and function calls) | ||
| 15 | |||
| 16 | (we did this already) | ||
| 17 | |||
| 18 | 1) Regular Expressions Test | ||
| 19 | |||
| 20 | Read a file of (extended per egrep) regular expressions (one per line), | ||
| 21 | and apply those to all files whose names are listed on the command line. | ||
| 22 | Basically, an 'egrep -f' simulator. Test it with 20 "vt100" patterns | ||
| 23 | against a five /etc/termcap files. Tests using more elaborate patters | ||
| 24 | would also be interesting. Your code should not break if given hundreds | ||
| 25 | of regular expressions or binary files to scan. | ||
| 26 | |||
| 27 | 2) Sorting Test | ||
| 28 | |||
| 29 | Sort an input file that consists of lines like this | ||
| 30 | |||
| 31 | var1=23 other=14 ditto=23 fred=2 | ||
| 32 | |||
| 33 | such that each output line is sorted WRT to the number. Order | ||
| 34 | of output lines does not change. Resolve collisions using the | ||
| 35 | variable name. e.g. | ||
| 36 | |||
| 37 | fred=2 other=14 ditto=23 var1=23 | ||
| 38 | |||
| 39 | Lines may be up to several kilobytes in length and contain | ||
| 40 | zillions of variables. | ||
| 41 | |||
| 42 | 3) System Test | ||
| 43 | |||
| 44 | Given a list of directories, report any bogus symbolic links contained | ||
| 45 | anywhere in those subtrees. A bogus symbolic link is one that cannot | ||
| 46 | be resolved because it points to a nonexistent or otherwise | ||
| 47 | unresolvable file. Do *not* use an external find executable. | ||
| 48 | Directories may be very very deep. Print a warning immediately if the | ||
| 49 | system you're running on doesn't support symbolic links. | ||
| 50 | |||
| 51 | |||
| 52 | I'll post perl solutions if people post the others. | ||
| 53 | |||
| 54 | |||
| 55 | --tom | ||
| 56 | -- | ||
| 57 | Tom Christiansen Perl Consultant, Gamer, Hiker tchrist@mox.perl.com | ||
| 58 | |||
| 59 | "But Billy! A *small* allowance prepares you for a lifetime of small | ||
| 60 | salaries and for your Social Security payments." --Family Circus | ||
| @@ -0,0 +1,4 @@ | |||
| 1 | ^def | ||
| 2 | ^class | ||
| 3 | ^import | ||
| 4 | ^from | ||
| @@ -0,0 +1,47 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # 1) Regular Expressions Test | ||
| 4 | # | ||
| 5 | # Read a file of (extended per egrep) regular expressions (one per line), | ||
| 6 | # and apply those to all files whose names are listed on the command line. | ||
| 7 | # Basically, an 'egrep -f' simulator. Test it with 20 "vt100" patterns | ||
| 8 | # against a five /etc/termcap files. Tests using more elaborate patters | ||
| 9 | # would also be interesting. Your code should not break if given hundreds | ||
| 10 | # of regular expressions or binary files to scan. | ||
| 11 | |||
| 12 | # This implementation: | ||
| 13 | # - combines all patterns into a single one using ( ... | ... | ... ) | ||
| 14 | # - reads patterns from stdin, scans files given as command line arguments | ||
| 15 | # - produces output in the format <file>:<lineno>:<line> | ||
| 16 | # - is only about 2.5 times as slow as egrep (though I couldn't run | ||
| 17 | # Tom's test -- this system, a vanilla SGI, only has /etc/terminfo) | ||
| 18 | |||
| 19 | import string | ||
| 20 | import sys | ||
| 21 | import re | ||
| 22 | |||
| 23 | def main(): | ||
| 24 | pats = map(chomp, sys.stdin.readlines()) | ||
| 25 | bigpat = '(' + '|'.join(pats) + ')' | ||
| 26 | prog = re.compile(bigpat) | ||
| 27 | |||
| 28 | for file in sys.argv[1:]: | ||
| 29 | try: | ||
| 30 | fp = open(file, 'r') | ||
| 31 | except IOError, msg: | ||
| 32 | print "%s: %s" % (file, msg) | ||
| 33 | continue | ||
| 34 | lineno = 0 | ||
| 35 | while 1: | ||
| 36 | line = fp.readline() | ||
| 37 | if not line: | ||
| 38 | break | ||
| 39 | lineno = lineno + 1 | ||
| 40 | if prog.search(line): | ||
| 41 | print "%s:%s:%s" % (file, lineno, line), | ||
| 42 | |||
| 43 | def chomp(s): | ||
| 44 | return s.rstrip('\n') | ||
| 45 | |||
| 46 | if __name__ == '__main__': | ||
| 47 | main() | ||
| @@ -0,0 +1,51 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # 2) Sorting Test | ||
| 4 | # | ||
| 5 | # Sort an input file that consists of lines like this | ||
| 6 | # | ||
| 7 | # var1=23 other=14 ditto=23 fred=2 | ||
| 8 | # | ||
| 9 | # such that each output line is sorted WRT to the number. Order | ||
| 10 | # of output lines does not change. Resolve collisions using the | ||
| 11 | # variable name. e.g. | ||
| 12 | # | ||
| 13 | # fred=2 other=14 ditto=23 var1=23 | ||
| 14 | # | ||
| 15 | # Lines may be up to several kilobytes in length and contain | ||
| 16 | # zillions of variables. | ||
| 17 | |||
| 18 | # This implementation: | ||
| 19 | # - Reads stdin, writes stdout | ||
| 20 | # - Uses any amount of whitespace to separate fields | ||
| 21 | # - Allows signed numbers | ||
| 22 | # - Treats illegally formatted fields as field=0 | ||
| 23 | # - Outputs the sorted fields with exactly one space between them | ||
| 24 | # - Handles blank input lines correctly | ||
| 25 | |||
| 26 | import re | ||
| 27 | import string | ||
| 28 | import sys | ||
| 29 | |||
| 30 | def main(): | ||
| 31 | prog = re.compile('^(.*)=([-+]?[0-9]+)') | ||
| 32 | def makekey(item, prog=prog): | ||
| 33 | match = prog.match(item) | ||
| 34 | if match: | ||
| 35 | var, num = match.group(1, 2) | ||
| 36 | return string.atoi(num), var | ||
| 37 | else: | ||
| 38 | # Bad input -- pretend it's a var with value 0 | ||
| 39 | return 0, item | ||
| 40 | while 1: | ||
| 41 | line = sys.stdin.readline() | ||
| 42 | if not line: | ||
| 43 | break | ||
| 44 | items = line.split() | ||
| 45 | items = map(makekey, items) | ||
| 46 | items.sort() | ||
| 47 | for num, var in items: | ||
| 48 | print "%s=%s" % (var, num), | ||
| 49 | |||
| 50 | |||
| 51 | main() | ||
| @@ -0,0 +1,74 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # 3) System Test | ||
| 4 | # | ||
| 5 | # Given a list of directories, report any bogus symbolic links contained | ||
| 6 | # anywhere in those subtrees. A bogus symbolic link is one that cannot | ||
| 7 | # be resolved because it points to a nonexistent or otherwise | ||
| 8 | # unresolvable file. Do *not* use an external find executable. | ||
| 9 | # Directories may be very very deep. Print a warning immediately if the | ||
| 10 | # system you're running on doesn't support symbolic links. | ||
| 11 | |||
| 12 | # This implementation: | ||
| 13 | # - takes one optional argument, using the current directory as default | ||
| 14 | # - uses chdir to increase performance | ||
| 15 | # - sorts the names per directory | ||
| 16 | # - prints output lines of the form "path1 -> path2" as it goes | ||
| 17 | # - prints error messages about directories it can't list or chdir into | ||
| 18 | |||
| 19 | import os | ||
| 20 | import sys | ||
| 21 | from stat import * | ||
| 22 | |||
| 23 | def main(): | ||
| 24 | try: | ||
| 25 | # Note: can't test for presence of lstat -- it's always there | ||
| 26 | dummy = os.readlink | ||
| 27 | except AttributeError: | ||
| 28 | print "This system doesn't have symbolic links" | ||
| 29 | sys.exit(0) | ||
| 30 | if sys.argv[1:]: | ||
| 31 | prefix = sys.argv[1] | ||
| 32 | else: | ||
| 33 | prefix = '' | ||
| 34 | if prefix: | ||
| 35 | os.chdir(prefix) | ||
| 36 | if prefix[-1:] != '/': prefix = prefix + '/' | ||
| 37 | reportboguslinks(prefix) | ||
| 38 | else: | ||
| 39 | reportboguslinks('') | ||
| 40 | |||
| 41 | def reportboguslinks(prefix): | ||
| 42 | try: | ||
| 43 | names = os.listdir('.') | ||
| 44 | except os.error, msg: | ||
| 45 | print "%s%s: can't list: %s" % (prefix, '.', msg) | ||
| 46 | return | ||
| 47 | names.sort() | ||
| 48 | for name in names: | ||
| 49 | if name == os.curdir or name == os.pardir: | ||
| 50 | continue | ||
| 51 | try: | ||
| 52 | mode = os.lstat(name)[ST_MODE] | ||
| 53 | except os.error: | ||
| 54 | print "%s%s: can't stat: %s" % (prefix, name, msg) | ||
| 55 | continue | ||
| 56 | if S_ISLNK(mode): | ||
| 57 | try: | ||
| 58 | os.stat(name) | ||
| 59 | except os.error: | ||
| 60 | print "%s%s -> %s" % \ | ||
| 61 | (prefix, name, os.readlink(name)) | ||
| 62 | elif S_ISDIR(mode): | ||
| 63 | try: | ||
| 64 | os.chdir(name) | ||
| 65 | except os.error, msg: | ||
| 66 | print "%s%s: can't chdir: %s" % \ | ||
| 67 | (prefix, name, msg) | ||
| 68 | continue | ||
| 69 | try: | ||
| 70 | reportboguslinks(prefix + name + '/') | ||
| 71 | finally: | ||
| 72 | os.chdir('..') | ||
| 73 | |||
| 74 | main() | ||
| @@ -0,0 +1,25 @@ | |||
| 1 | This is a collection of demos and tests for the curses module. | ||
| 2 | |||
| 3 | ncurses demos | ||
| 4 | ============= | ||
| 5 | |||
| 6 | These demos are converted from the C versions in the ncurses | ||
| 7 | distribution, and were contributed by Thomas Gellekum <tg@FreeBSD.org> | ||
| 8 | I didn't strive for a `pythonic' style, but bluntly copied the | ||
| 9 | originals. I won't attempt to `beautify' the program anytime soon, but | ||
| 10 | I wouldn't mind someone else making an effort in that direction, of | ||
| 11 | course. | ||
| 12 | |||
| 13 | ncurses.py -- currently only a panels demo | ||
| 14 | rain.py -- raindrops keep falling on my desktop | ||
| 15 | tclock.py -- ASCII clock, by Howard Jones | ||
| 16 | xmas.py -- I'm dreaming of an ASCII christmas | ||
| 17 | |||
| 18 | Please submit bugfixes and new contributions to the Python bug tracker. | ||
| 19 | |||
| 20 | |||
| 21 | Other demos | ||
| 22 | =========== | ||
| 23 | |||
| 24 | life.py -- Simple game of Life | ||
| 25 | repeat.py -- Repeatedly execute a shell command (like watch(1)) | ||
| @@ -0,0 +1,216 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # life.py -- A curses-based version of Conway's Game of Life. | ||
| 3 | # Contributed by AMK | ||
| 4 | # | ||
| 5 | # An empty board will be displayed, and the following commands are available: | ||
| 6 | # E : Erase the board | ||
| 7 | # R : Fill the board randomly | ||
| 8 | # S : Step for a single generation | ||
| 9 | # C : Update continuously until a key is struck | ||
| 10 | # Q : Quit | ||
| 11 | # Cursor keys : Move the cursor around the board | ||
| 12 | # Space or Enter : Toggle the contents of the cursor's position | ||
| 13 | # | ||
| 14 | # TODO : | ||
| 15 | # Support the mouse | ||
| 16 | # Use colour if available | ||
| 17 | # Make board updates faster | ||
| 18 | # | ||
| 19 | |||
| 20 | import random, string, traceback | ||
| 21 | import curses | ||
| 22 | |||
| 23 | class LifeBoard: | ||
| 24 | """Encapsulates a Life board | ||
| 25 | |||
| 26 | Attributes: | ||
| 27 | X,Y : horizontal and vertical size of the board | ||
| 28 | state : dictionary mapping (x,y) to 0 or 1 | ||
| 29 | |||
| 30 | Methods: | ||
| 31 | display(update_board) -- If update_board is true, compute the | ||
| 32 | next generation. Then display the state | ||
| 33 | of the board and refresh the screen. | ||
| 34 | erase() -- clear the entire board | ||
| 35 | makeRandom() -- fill the board randomly | ||
| 36 | set(y,x) -- set the given cell to Live; doesn't refresh the screen | ||
| 37 | toggle(y,x) -- change the given cell from live to dead, or vice | ||
| 38 | versa, and refresh the screen display | ||
| 39 | |||
| 40 | """ | ||
| 41 | def __init__(self, scr, char=ord('*')): | ||
| 42 | """Create a new LifeBoard instance. | ||
| 43 | |||
| 44 | scr -- curses screen object to use for display | ||
| 45 | char -- character used to render live cells (default: '*') | ||
| 46 | """ | ||
| 47 | self.state = {} | ||
| 48 | self.scr = scr | ||
| 49 | Y, X = self.scr.getmaxyx() | ||
| 50 | self.X, self.Y = X-2, Y-2-1 | ||
| 51 | self.char = char | ||
| 52 | self.scr.clear() | ||
| 53 | |||
| 54 | # Draw a border around the board | ||
| 55 | border_line = '+'+(self.X*'-')+'+' | ||
| 56 | self.scr.addstr(0, 0, border_line) | ||
| 57 | self.scr.addstr(self.Y+1,0, border_line) | ||
| 58 | for y in range(0, self.Y): | ||
| 59 | self.scr.addstr(1+y, 0, '|') | ||
| 60 | self.scr.addstr(1+y, self.X+1, '|') | ||
| 61 | self.scr.refresh() | ||
| 62 | |||
| 63 | def set(self, y, x): | ||
| 64 | """Set a cell to the live state""" | ||
| 65 | if x<0 or self.X<=x or y<0 or self.Y<=y: | ||
| 66 | raise ValueError, "Coordinates out of range %i,%i"% (y,x) | ||
| 67 | self.state[x,y] = 1 | ||
| 68 | |||
| 69 | def toggle(self, y, x): | ||
| 70 | """Toggle a cell's state between live and dead""" | ||
| 71 | if x<0 or self.X<=x or y<0 or self.Y<=y: | ||
| 72 | raise ValueError, "Coordinates out of range %i,%i"% (y,x) | ||
| 73 | if self.state.has_key( (x,y) ): | ||
| 74 | del self.state[x,y] | ||
| 75 | self.scr.addch(y+1, x+1, ' ') | ||
| 76 | else: | ||
| 77 | self.state[x,y] = 1 | ||
| 78 | self.scr.addch(y+1, x+1, self.char) | ||
| 79 | self.scr.refresh() | ||
| 80 | |||
| 81 | def erase(self): | ||
| 82 | """Clear the entire board and update the board display""" | ||
| 83 | self.state = {} | ||
| 84 | self.display(update_board=False) | ||
| 85 | |||
| 86 | def display(self, update_board=True): | ||
| 87 | """Display the whole board, optionally computing one generation""" | ||
| 88 | M,N = self.X, self.Y | ||
| 89 | if not update_board: | ||
| 90 | for i in range(0, M): | ||
| 91 | for j in range(0, N): | ||
| 92 | if self.state.has_key( (i,j) ): | ||
| 93 | self.scr.addch(j+1, i+1, self.char) | ||
| 94 | else: | ||
| 95 | self.scr.addch(j+1, i+1, ' ') | ||
| 96 | self.scr.refresh() | ||
| 97 | return | ||
| 98 | |||
| 99 | d = {} | ||
| 100 | self.boring = 1 | ||
| 101 | for i in range(0, M): | ||
| 102 | L = range( max(0, i-1), min(M, i+2) ) | ||
| 103 | for j in range(0, N): | ||
| 104 | s = 0 | ||
| 105 | live = self.state.has_key( (i,j) ) | ||
| 106 | for k in range( max(0, j-1), min(N, j+2) ): | ||
| 107 | for l in L: | ||
| 108 | if self.state.has_key( (l,k) ): | ||
| 109 | s += 1 | ||
| 110 | s -= live | ||
| 111 | if s == 3: | ||
| 112 | # Birth | ||
| 113 | d[i,j] = 1 | ||
| 114 | self.scr.addch(j+1, i+1, self.char) | ||
| 115 | if not live: self.boring = 0 | ||
| 116 | elif s == 2 and live: d[i,j] = 1 # Survival | ||
| 117 | elif live: | ||
| 118 | # Death | ||
| 119 | self.scr.addch(j+1, i+1, ' ') | ||
| 120 | self.boring = 0 | ||
| 121 | self.state = d | ||
| 122 | self.scr.refresh() | ||
| 123 | |||
| 124 | def makeRandom(self): | ||
| 125 | "Fill the board with a random pattern" | ||
| 126 | self.state = {} | ||
| 127 | for i in range(0, self.X): | ||
| 128 | for j in range(0, self.Y): | ||
| 129 | if random.random() > 0.5: | ||
| 130 | self.set(j,i) | ||
| 131 | |||
| 132 | |||
| 133 | def erase_menu(stdscr, menu_y): | ||
| 134 | "Clear the space where the menu resides" | ||
| 135 | stdscr.move(menu_y, 0) | ||
| 136 | stdscr.clrtoeol() | ||
| 137 | stdscr.move(menu_y+1, 0) | ||
| 138 | stdscr.clrtoeol() | ||
| 139 | |||
| 140 | def display_menu(stdscr, menu_y): | ||
| 141 | "Display the menu of possible keystroke commands" | ||
| 142 | erase_menu(stdscr, menu_y) | ||
| 143 | stdscr.addstr(menu_y, 4, | ||
| 144 | 'Use the cursor keys to move, and space or Enter to toggle a cell.') | ||
| 145 | stdscr.addstr(menu_y+1, 4, | ||
| 146 | 'E)rase the board, R)andom fill, S)tep once or C)ontinuously, Q)uit') | ||
| 147 | |||
| 148 | def keyloop(stdscr): | ||
| 149 | # Clear the screen and display the menu of keys | ||
| 150 | stdscr.clear() | ||
| 151 | stdscr_y, stdscr_x = stdscr.getmaxyx() | ||
| 152 | menu_y = (stdscr_y-3)-1 | ||
| 153 | display_menu(stdscr, menu_y) | ||
| 154 | |||
| 155 | # Allocate a subwindow for the Life board and create the board object | ||
| 156 | subwin = stdscr.subwin(stdscr_y-3, stdscr_x, 0, 0) | ||
| 157 | board = LifeBoard(subwin, char=ord('*')) | ||
| 158 | board.display(update_board=False) | ||
| 159 | |||
| 160 | # xpos, ypos are the cursor's position | ||
| 161 | xpos, ypos = board.X/2, board.Y/2 | ||
| 162 | |||
| 163 | # Main loop: | ||
| 164 | while (1): | ||
| 165 | stdscr.move(1+ypos, 1+xpos) # Move the cursor | ||
| 166 | c = stdscr.getch() # Get a keystroke | ||
| 167 | if 0<c<256: | ||
| 168 | c = chr(c) | ||
| 169 | if c in ' \n': | ||
| 170 | board.toggle(ypos, xpos) | ||
| 171 | elif c in 'Cc': | ||
| 172 | erase_menu(stdscr, menu_y) | ||
| 173 | stdscr.addstr(menu_y, 6, ' Hit any key to stop continuously ' | ||
| 174 | 'updating the screen.') | ||
| 175 | stdscr.refresh() | ||
| 176 | # Activate nodelay mode; getch() will return -1 | ||
| 177 | # if no keystroke is available, instead of waiting. | ||
| 178 | stdscr.nodelay(1) | ||
| 179 | while (1): | ||
| 180 | c = stdscr.getch() | ||
| 181 | if c != -1: | ||
| 182 | break | ||
| 183 | stdscr.addstr(0,0, '/') | ||
| 184 | stdscr.refresh() | ||
| 185 | board.display() | ||
| 186 | stdscr.addstr(0,0, '+') | ||
| 187 | stdscr.refresh() | ||
| 188 | |||
| 189 | stdscr.nodelay(0) # Disable nodelay mode | ||
| 190 | display_menu(stdscr, menu_y) | ||
| 191 | |||
| 192 | elif c in 'Ee': | ||
| 193 | board.erase() | ||
| 194 | elif c in 'Qq': | ||
| 195 | break | ||
| 196 | elif c in 'Rr': | ||
| 197 | board.makeRandom() | ||
| 198 | board.display(update_board=False) | ||
| 199 | elif c in 'Ss': | ||
| 200 | board.display() | ||
| 201 | else: pass # Ignore incorrect keys | ||
| 202 | elif c == curses.KEY_UP and ypos>0: ypos -= 1 | ||
| 203 | elif c == curses.KEY_DOWN and ypos<board.Y-1: ypos += 1 | ||
| 204 | elif c == curses.KEY_LEFT and xpos>0: xpos -= 1 | ||
| 205 | elif c == curses.KEY_RIGHT and xpos<board.X-1: xpos += 1 | ||
| 206 | else: | ||
| 207 | # Ignore incorrect keys | ||
| 208 | pass | ||
| 209 | |||
| 210 | |||
| 211 | def main(stdscr): | ||
| 212 | keyloop(stdscr) # Enter the main loop | ||
| 213 | |||
| 214 | |||
| 215 | if __name__ == '__main__': | ||
| 216 | curses.wrapper(main) | ||
| @@ -0,0 +1,273 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # | ||
| 3 | # $Id: ncurses.py 36559 2004-07-18 05:56:09Z tim_one $ | ||
| 4 | # | ||
| 5 | # (n)curses exerciser in Python, an interactive test for the curses | ||
| 6 | # module. Currently, only the panel demos are ported. | ||
| 7 | |||
| 8 | import curses | ||
| 9 | from curses import panel | ||
| 10 | |||
| 11 | def wGetchar(win = None): | ||
| 12 | if win == None: win = stdscr | ||
| 13 | return win.getch() | ||
| 14 | |||
| 15 | def Getchar(): | ||
| 16 | wGetchar() | ||
| 17 | |||
| 18 | # | ||
| 19 | # Panels tester | ||
| 20 | # | ||
| 21 | def wait_a_while(): | ||
| 22 | if nap_msec == 1: | ||
| 23 | Getchar() | ||
| 24 | else: | ||
| 25 | curses.napms(nap_msec) | ||
| 26 | |||
| 27 | def saywhat(text): | ||
| 28 | stdscr.move(curses.LINES - 1, 0) | ||
| 29 | stdscr.clrtoeol() | ||
| 30 | stdscr.addstr(text) | ||
| 31 | |||
| 32 | def mkpanel(color, rows, cols, tly, tlx): | ||
| 33 | win = curses.newwin(rows, cols, tly, tlx) | ||
| 34 | pan = panel.new_panel(win) | ||
| 35 | if curses.has_colors(): | ||
| 36 | if color == curses.COLOR_BLUE: | ||
| 37 | fg = curses.COLOR_WHITE | ||
| 38 | else: | ||
| 39 | fg = curses.COLOR_BLACK | ||
| 40 | bg = color | ||
| 41 | curses.init_pair(color, fg, bg) | ||
| 42 | win.bkgdset(ord(' '), curses.color_pair(color)) | ||
| 43 | else: | ||
| 44 | win.bkgdset(ord(' '), curses.A_BOLD) | ||
| 45 | |||
| 46 | return pan | ||
| 47 | |||
| 48 | def pflush(): | ||
| 49 | panel.update_panels() | ||
| 50 | curses.doupdate() | ||
| 51 | |||
| 52 | def fill_panel(pan): | ||
| 53 | win = pan.window() | ||
| 54 | num = pan.userptr()[1] | ||
| 55 | |||
| 56 | win.move(1, 1) | ||
| 57 | win.addstr("-pan%c-" % num) | ||
| 58 | win.clrtoeol() | ||
| 59 | win.box() | ||
| 60 | |||
| 61 | maxy, maxx = win.getmaxyx() | ||
| 62 | for y in range(2, maxy - 1): | ||
| 63 | for x in range(1, maxx - 1): | ||
| 64 | win.move(y, x) | ||
| 65 | win.addch(num) | ||
| 66 | |||
| 67 | def demo_panels(win): | ||
| 68 | global stdscr, nap_msec, mod | ||
| 69 | stdscr = win | ||
| 70 | nap_msec = 1 | ||
| 71 | mod = ["test", "TEST", "(**)", "*()*", "<-->", "LAST"] | ||
| 72 | |||
| 73 | stdscr.refresh() | ||
| 74 | |||
| 75 | for y in range(0, curses.LINES - 1): | ||
| 76 | for x in range(0, curses.COLS): | ||
| 77 | stdscr.addstr("%d" % ((y + x) % 10)) | ||
| 78 | for y in range(0, 1): | ||
| 79 | p1 = mkpanel(curses.COLOR_RED, | ||
| 80 | curses.LINES / 2 - 2, | ||
| 81 | curses.COLS / 8 + 1, | ||
| 82 | 0, | ||
| 83 | 0) | ||
| 84 | p1.set_userptr("p1") | ||
| 85 | |||
| 86 | p2 = mkpanel(curses.COLOR_GREEN, | ||
| 87 | curses.LINES / 2 + 1, | ||
| 88 | curses.COLS / 7, | ||
| 89 | curses.LINES / 4, | ||
| 90 | curses.COLS / 10) | ||
| 91 | p2.set_userptr("p2") | ||
| 92 | |||
| 93 | p3 = mkpanel(curses.COLOR_YELLOW, | ||
| 94 | curses.LINES / 4, | ||
| 95 | curses.COLS / 10, | ||
| 96 | curses.LINES / 2, | ||
| 97 | curses.COLS / 9) | ||
| 98 | p3.set_userptr("p3") | ||
| 99 | |||
| 100 | p4 = mkpanel(curses.COLOR_BLUE, | ||
| 101 | curses.LINES / 2 - 2, | ||
| 102 | curses.COLS / 8, | ||
| 103 | curses.LINES / 2 - 2, | ||
| 104 | curses.COLS / 3) | ||
| 105 | p4.set_userptr("p4") | ||
| 106 | |||
| 107 | p5 = mkpanel(curses.COLOR_MAGENTA, | ||
| 108 | curses.LINES / 2 - 2, | ||
| 109 | curses.COLS / 8, | ||
| 110 | curses.LINES / 2, | ||
| 111 | curses.COLS / 2 - 2) | ||
| 112 | p5.set_userptr("p5") | ||
| 113 | |||
| 114 | fill_panel(p1) | ||
| 115 | fill_panel(p2) | ||
| 116 | fill_panel(p3) | ||
| 117 | fill_panel(p4) | ||
| 118 | fill_panel(p5) | ||
| 119 | p4.hide() | ||
| 120 | p5.hide() | ||
| 121 | pflush() | ||
| 122 | saywhat("press any key to continue") | ||
| 123 | wait_a_while() | ||
| 124 | |||
| 125 | saywhat("h3 s1 s2 s4 s5;press any key to continue") | ||
| 126 | p1.move(0, 0) | ||
| 127 | p3.hide() | ||
| 128 | p1.show() | ||
| 129 | p2.show() | ||
| 130 | p4.show() | ||
| 131 | p5.show() | ||
| 132 | pflush() | ||
| 133 | wait_a_while() | ||
| 134 | |||
| 135 | saywhat("s1; press any key to continue") | ||
| 136 | p1.show() | ||
| 137 | pflush() | ||
| 138 | wait_a_while() | ||
| 139 | |||
| 140 | saywhat("s2; press any key to continue") | ||
| 141 | p2.show() | ||
| 142 | pflush() | ||
| 143 | wait_a_while() | ||
| 144 | |||
| 145 | saywhat("m2; press any key to continue") | ||
| 146 | p2.move(curses.LINES / 3 + 1, curses.COLS / 8) | ||
| 147 | pflush() | ||
| 148 | wait_a_while() | ||
| 149 | |||
| 150 | saywhat("s3; press any key to continue") | ||
| 151 | p3.show() | ||
| 152 | pflush() | ||
| 153 | wait_a_while() | ||
| 154 | |||
| 155 | saywhat("m3; press any key to continue") | ||
| 156 | p3.move(curses.LINES / 4 + 1, curses.COLS / 15) | ||
| 157 | pflush() | ||
| 158 | wait_a_while() | ||
| 159 | |||
| 160 | saywhat("b3; press any key to continue") | ||
| 161 | p3.bottom() | ||
| 162 | pflush() | ||
| 163 | wait_a_while() | ||
| 164 | |||
| 165 | saywhat("s4; press any key to continue") | ||
| 166 | p4.show() | ||
| 167 | pflush() | ||
| 168 | wait_a_while() | ||
| 169 | |||
| 170 | saywhat("s5; press any key to continue") | ||
| 171 | p5.show() | ||
| 172 | pflush() | ||
| 173 | wait_a_while() | ||
| 174 | |||
| 175 | saywhat("t3; press any key to continue") | ||
| 176 | p3.top() | ||
| 177 | pflush() | ||
| 178 | wait_a_while() | ||
| 179 | |||
| 180 | saywhat("t1; press any key to continue") | ||
| 181 | p1.show() | ||
| 182 | pflush() | ||
| 183 | wait_a_while() | ||
| 184 | |||
| 185 | saywhat("t2; press any key to continue") | ||
| 186 | p2.show() | ||
| 187 | pflush() | ||
| 188 | wait_a_while() | ||
| 189 | |||
| 190 | saywhat("t3; press any key to continue") | ||
| 191 | p3.show() | ||
| 192 | pflush() | ||
| 193 | wait_a_while() | ||
| 194 | |||
| 195 | saywhat("t4; press any key to continue") | ||
| 196 | p4.show() | ||
| 197 | pflush() | ||
| 198 | wait_a_while() | ||
| 199 | |||
| 200 | for itmp in range(0, 6): | ||
| 201 | w4 = p4.window() | ||
| 202 | w5 = p5.window() | ||
| 203 | |||
| 204 | saywhat("m4; press any key to continue") | ||
| 205 | w4.move(curses.LINES / 8, 1) | ||
| 206 | w4.addstr(mod[itmp]) | ||
| 207 | p4.move(curses.LINES / 6, itmp * curses.COLS / 8) | ||
| 208 | w5.move(curses.LINES / 6, 1) | ||
| 209 | w5.addstr(mod[itmp]) | ||
| 210 | pflush() | ||
| 211 | wait_a_while() | ||
| 212 | |||
| 213 | saywhat("m5; press any key to continue") | ||
| 214 | w4.move(curses.LINES / 6, 1) | ||
| 215 | w4.addstr(mod[itmp]) | ||
| 216 | p5.move(curses.LINES / 3 - 1, itmp * 10 + 6) | ||
| 217 | w5.move(curses.LINES / 8, 1) | ||
| 218 | w5.addstr(mod[itmp]) | ||
| 219 | pflush() | ||
| 220 | wait_a_while() | ||
| 221 | |||
| 222 | saywhat("m4; press any key to continue") | ||
| 223 | p4.move(curses.LINES / 6, (itmp + 1) * curses.COLS / 8) | ||
| 224 | pflush() | ||
| 225 | wait_a_while() | ||
| 226 | |||
| 227 | saywhat("t5; press any key to continue") | ||
| 228 | p5.top() | ||
| 229 | pflush() | ||
| 230 | wait_a_while() | ||
| 231 | |||
| 232 | saywhat("t2; press any key to continue") | ||
| 233 | p2.top() | ||
| 234 | pflush() | ||
| 235 | wait_a_while() | ||
| 236 | |||
| 237 | saywhat("t1; press any key to continue") | ||
| 238 | p1.top() | ||
| 239 | pflush() | ||
| 240 | wait_a_while() | ||
| 241 | |||
| 242 | saywhat("d2; press any key to continue") | ||
| 243 | del p2 | ||
| 244 | pflush() | ||
| 245 | wait_a_while() | ||
| 246 | |||
| 247 | saywhat("h3; press any key to continue") | ||
| 248 | p3.hide() | ||
| 249 | pflush() | ||
| 250 | wait_a_while() | ||
| 251 | |||
| 252 | saywhat("d1; press any key to continue") | ||
| 253 | del p1 | ||
| 254 | pflush() | ||
| 255 | wait_a_while() | ||
| 256 | |||
| 257 | saywhat("d4; press any key to continue") | ||
| 258 | del p4 | ||
| 259 | pflush() | ||
| 260 | wait_a_while() | ||
| 261 | |||
| 262 | saywhat("d5; press any key to continue") | ||
| 263 | del p5 | ||
| 264 | pflush() | ||
| 265 | wait_a_while() | ||
| 266 | if nap_msec == 1: | ||
| 267 | break | ||
| 268 | nap_msec = 100 | ||
| 269 | |||
| 270 | # | ||
| 271 | # one fine day there'll be the menu at this place | ||
| 272 | # | ||
| 273 | curses.wrapper(demo_panels) | ||
| @@ -0,0 +1,94 @@ | |||
| 1 | #!/bin/python | ||
| 2 | # | ||
| 3 | # $Id: rain.py 46625 2006-06-03 23:02:15Z andrew.kuchling $ | ||
| 4 | # | ||
| 5 | # somebody should probably check the randrange()s... | ||
| 6 | |||
| 7 | import curses | ||
| 8 | from random import randrange | ||
| 9 | |||
| 10 | def next_j(j): | ||
| 11 | if j == 0: | ||
| 12 | j = 4 | ||
| 13 | else: | ||
| 14 | j -= 1 | ||
| 15 | |||
| 16 | if curses.has_colors(): | ||
| 17 | z = randrange(0, 3) | ||
| 18 | color = curses.color_pair(z) | ||
| 19 | if z: | ||
| 20 | color = color | curses.A_BOLD | ||
| 21 | stdscr.attrset(color) | ||
| 22 | |||
| 23 | return j | ||
| 24 | |||
| 25 | def main(win): | ||
| 26 | # we know that the first argument from curses.wrapper() is stdscr. | ||
| 27 | # Initialize it globally for convenience. | ||
| 28 | global stdscr | ||
| 29 | stdscr = win | ||
| 30 | |||
| 31 | if curses.has_colors(): | ||
| 32 | bg = curses.COLOR_BLACK | ||
| 33 | curses.init_pair(1, curses.COLOR_BLUE, bg) | ||
| 34 | curses.init_pair(2, curses.COLOR_CYAN, bg) | ||
| 35 | |||
| 36 | curses.nl() | ||
| 37 | curses.noecho() | ||
| 38 | # XXX curs_set() always returns ERR | ||
| 39 | # curses.curs_set(0) | ||
| 40 | stdscr.timeout(0) | ||
| 41 | |||
| 42 | c = curses.COLS - 4 | ||
| 43 | r = curses.LINES - 4 | ||
| 44 | xpos = [0] * c | ||
| 45 | ypos = [0] * r | ||
| 46 | for j in range(4, -1, -1): | ||
| 47 | xpos[j] = randrange(0, c) + 2 | ||
| 48 | ypos[j] = randrange(0, r) + 2 | ||
| 49 | |||
| 50 | j = 0 | ||
| 51 | while True: | ||
| 52 | x = randrange(0, c) + 2 | ||
| 53 | y = randrange(0, r) + 2 | ||
| 54 | |||
| 55 | stdscr.addch(y, x, ord('.')) | ||
| 56 | |||
| 57 | stdscr.addch(ypos[j], xpos[j], ord('o')) | ||
| 58 | |||
| 59 | j = next_j(j) | ||
| 60 | stdscr.addch(ypos[j], xpos[j], ord('O')) | ||
| 61 | |||
| 62 | j = next_j(j) | ||
| 63 | stdscr.addch( ypos[j] - 1, xpos[j], ord('-')) | ||
| 64 | stdscr.addstr(ypos[j], xpos[j] - 1, "|.|") | ||
| 65 | stdscr.addch( ypos[j] + 1, xpos[j], ord('-')) | ||
| 66 | |||
| 67 | j = next_j(j) | ||
| 68 | stdscr.addch( ypos[j] - 2, xpos[j], ord('-')) | ||
| 69 | stdscr.addstr(ypos[j] - 1, xpos[j] - 1, "/ \\") | ||
| 70 | stdscr.addstr(ypos[j], xpos[j] - 2, "| O |") | ||
| 71 | stdscr.addstr(ypos[j] + 1, xpos[j] - 1, "\\ /") | ||
| 72 | stdscr.addch( ypos[j] + 2, xpos[j], ord('-')) | ||
| 73 | |||
| 74 | j = next_j(j) | ||
| 75 | stdscr.addch( ypos[j] - 2, xpos[j], ord(' ')) | ||
| 76 | stdscr.addstr(ypos[j] - 1, xpos[j] - 1, " ") | ||
| 77 | stdscr.addstr(ypos[j], xpos[j] - 2, " ") | ||
| 78 | stdscr.addstr(ypos[j] + 1, xpos[j] - 1, " ") | ||
| 79 | stdscr.addch( ypos[j] + 2, xpos[j], ord(' ')) | ||
| 80 | |||
| 81 | xpos[j] = x | ||
| 82 | ypos[j] = y | ||
| 83 | |||
| 84 | ch = stdscr.getch() | ||
| 85 | if ch == ord('q') or ch == ord('Q'): | ||
| 86 | return | ||
| 87 | elif ch == ord('s'): | ||
| 88 | stdscr.nodelay(0) | ||
| 89 | elif ch == ord(' '): | ||
| 90 | stdscr.nodelay(1) | ||
| 91 | |||
| 92 | curses.napms(50) | ||
| 93 | |||
| 94 | curses.wrapper(main) | ||
| @@ -0,0 +1,58 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """repeat <shell-command> | ||
| 4 | |||
| 5 | This simple program repeatedly (at 1-second intervals) executes the | ||
| 6 | shell command given on the command line and displays the output (or as | ||
| 7 | much of it as fits on the screen). It uses curses to paint each new | ||
| 8 | output on top of the old output, so that if nothing changes, the | ||
| 9 | screen doesn't change. This is handy to watch for changes in e.g. a | ||
| 10 | directory or process listing. | ||
| 11 | |||
| 12 | To end, hit Control-C. | ||
| 13 | """ | ||
| 14 | |||
| 15 | # Author: Guido van Rossum | ||
| 16 | |||
| 17 | # Disclaimer: there's a Linux program named 'watch' that does the same | ||
| 18 | # thing. Honestly, I didn't know of its existence when I wrote this! | ||
| 19 | |||
| 20 | # To do: add features until it has the same functionality as watch(1); | ||
| 21 | # then compare code size and development time. | ||
| 22 | |||
| 23 | import os | ||
| 24 | import sys | ||
| 25 | import time | ||
| 26 | import curses | ||
| 27 | |||
| 28 | def main(): | ||
| 29 | if not sys.argv[1:]: | ||
| 30 | print __doc__ | ||
| 31 | sys.exit(0) | ||
| 32 | cmd = " ".join(sys.argv[1:]) | ||
| 33 | p = os.popen(cmd, "r") | ||
| 34 | text = p.read() | ||
| 35 | sts = p.close() | ||
| 36 | if sts: | ||
| 37 | print >>sys.stderr, "Exit code:", sts | ||
| 38 | sys.exit(sts) | ||
| 39 | w = curses.initscr() | ||
| 40 | try: | ||
| 41 | while True: | ||
| 42 | w.erase() | ||
| 43 | try: | ||
| 44 | w.addstr(text) | ||
| 45 | except curses.error: | ||
| 46 | pass | ||
| 47 | w.refresh() | ||
| 48 | time.sleep(1) | ||
| 49 | p = os.popen(cmd, "r") | ||
| 50 | text = p.read() | ||
| 51 | sts = p.close() | ||
| 52 | if sts: | ||
| 53 | print >>sys.stderr, "Exit code:", sts | ||
| 54 | sys.exit(sts) | ||
| 55 | finally: | ||
| 56 | curses.endwin() | ||
| 57 | |||
| 58 | main() | ||
| @@ -0,0 +1,147 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # | ||
| 3 | # $Id: tclock.py 46626 2006-06-03 23:07:21Z andrew.kuchling $ | ||
| 4 | # | ||
| 5 | # From tclock.c, Copyright Howard Jones <ha.jones@ic.ac.uk>, September 1994. | ||
| 6 | |||
| 7 | from math import * | ||
| 8 | import curses, time | ||
| 9 | |||
| 10 | ASPECT = 2.2 | ||
| 11 | |||
| 12 | def sign(_x): | ||
| 13 | if _x < 0: return -1 | ||
| 14 | return 1 | ||
| 15 | |||
| 16 | def A2XY(angle, radius): | ||
| 17 | return (int(round(ASPECT * radius * sin(angle))), | ||
| 18 | int(round(radius * cos(angle)))) | ||
| 19 | |||
| 20 | def plot(x, y, col): | ||
| 21 | stdscr.addch(y, x, col) | ||
| 22 | |||
| 23 | # draw a diagonal line using Bresenham's algorithm | ||
| 24 | def dline(pair, from_x, from_y, x2, y2, ch): | ||
| 25 | if curses.has_colors(): | ||
| 26 | stdscr.attrset(curses.color_pair(pair)) | ||
| 27 | |||
| 28 | dx = x2 - from_x | ||
| 29 | dy = y2 - from_y | ||
| 30 | |||
| 31 | ax = abs(dx * 2) | ||
| 32 | ay = abs(dy * 2) | ||
| 33 | |||
| 34 | sx = sign(dx) | ||
| 35 | sy = sign(dy) | ||
| 36 | |||
| 37 | x = from_x | ||
| 38 | y = from_y | ||
| 39 | |||
| 40 | if ax > ay: | ||
| 41 | d = ay - ax // 2 | ||
| 42 | |||
| 43 | while True: | ||
| 44 | plot(x, y, ch) | ||
| 45 | if x == x2: | ||
| 46 | return | ||
| 47 | |||
| 48 | if d >= 0: | ||
| 49 | y += sy | ||
| 50 | d -= ax | ||
| 51 | x += sx | ||
| 52 | d += ay | ||
| 53 | else: | ||
| 54 | d = ax - ay // 2 | ||
| 55 | |||
| 56 | while True: | ||
| 57 | plot(x, y, ch) | ||
| 58 | if y == y2: | ||
| 59 | return | ||
| 60 | |||
| 61 | if d >= 0: | ||
| 62 | x += sx | ||
| 63 | d -= ay | ||
| 64 | y += sy | ||
| 65 | d += ax | ||
| 66 | |||
| 67 | def main(win): | ||
| 68 | global stdscr | ||
| 69 | stdscr = win | ||
| 70 | |||
| 71 | lastbeep = -1 | ||
| 72 | my_bg = curses.COLOR_BLACK | ||
| 73 | |||
| 74 | stdscr.nodelay(1) | ||
| 75 | stdscr.timeout(0) | ||
| 76 | # curses.curs_set(0) | ||
| 77 | if curses.has_colors(): | ||
| 78 | curses.init_pair(1, curses.COLOR_RED, my_bg) | ||
| 79 | curses.init_pair(2, curses.COLOR_MAGENTA, my_bg) | ||
| 80 | curses.init_pair(3, curses.COLOR_GREEN, my_bg) | ||
| 81 | |||
| 82 | cx = (curses.COLS - 1) // 2 | ||
| 83 | cy = curses.LINES // 2 | ||
| 84 | ch = min( cy-1, int(cx // ASPECT) - 1) | ||
| 85 | mradius = (3 * ch) // 4 | ||
| 86 | hradius = ch // 2 | ||
| 87 | sradius = 5 * ch // 6 | ||
| 88 | |||
| 89 | for i in range(0, 12): | ||
| 90 | sangle = (i + 1) * 2.0 * pi / 12.0 | ||
| 91 | sdx, sdy = A2XY(sangle, sradius) | ||
| 92 | |||
| 93 | stdscr.addstr(cy - sdy, cx + sdx, "%d" % (i + 1)) | ||
| 94 | |||
| 95 | stdscr.addstr(0, 0, | ||
| 96 | "ASCII Clock by Howard Jones <ha.jones@ic.ac.uk>, 1994") | ||
| 97 | |||
| 98 | sradius = max(sradius-4, 8) | ||
| 99 | |||
| 100 | while True: | ||
| 101 | curses.napms(1000) | ||
| 102 | |||
| 103 | tim = time.time() | ||
| 104 | t = time.localtime(tim) | ||
| 105 | |||
| 106 | hours = t[3] + t[4] / 60.0 | ||
| 107 | if hours > 12.0: | ||
| 108 | hours -= 12.0 | ||
| 109 | |||
| 110 | mangle = t[4] * 2 * pi / 60.0 | ||
| 111 | mdx, mdy = A2XY(mangle, mradius) | ||
| 112 | |||
| 113 | hangle = hours * 2 * pi / 12.0 | ||
| 114 | hdx, hdy = A2XY(hangle, hradius) | ||
| 115 | |||
| 116 | sangle = t[5] * 2 * pi / 60.0 | ||
| 117 | sdx, sdy = A2XY(sangle, sradius) | ||
| 118 | |||
| 119 | dline(3, cx, cy, cx + mdx, cy - mdy, ord('#')) | ||
| 120 | |||
| 121 | stdscr.attrset(curses.A_REVERSE) | ||
| 122 | dline(2, cx, cy, cx + hdx, cy - hdy, ord('.')) | ||
| 123 | stdscr.attroff(curses.A_REVERSE) | ||
| 124 | |||
| 125 | if curses.has_colors(): | ||
| 126 | stdscr.attrset(curses.color_pair(1)) | ||
| 127 | |||
| 128 | plot(cx + sdx, cy - sdy, ord('O')) | ||
| 129 | |||
| 130 | if curses.has_colors(): | ||
| 131 | stdscr.attrset(curses.color_pair(0)) | ||
| 132 | |||
| 133 | stdscr.addstr(curses.LINES - 2, 0, time.ctime(tim)) | ||
| 134 | stdscr.refresh() | ||
| 135 | if (t[5] % 5) == 0 and t[5] != lastbeep: | ||
| 136 | lastbeep = t[5] | ||
| 137 | curses.beep() | ||
| 138 | |||
| 139 | ch = stdscr.getch() | ||
| 140 | if ch == ord('q'): | ||
| 141 | return 0 | ||
| 142 | |||
| 143 | plot(cx + sdx, cy - sdy, ord(' ')) | ||
| 144 | dline(0, cx, cy, cx + hdx, cy - hdy, ord(' ')) | ||
| 145 | dline(0, cx, cy, cx + mdx, cy - mdy, ord(' ')) | ||
| 146 | |||
| 147 | curses.wrapper(main) | ||
| @@ -0,0 +1,906 @@ | |||
| 1 | # asciixmas | ||
| 2 | # December 1989 Larry Bartz Indianapolis, IN | ||
| 3 | # | ||
| 4 | # $Id: xmas.py 46623 2006-06-03 22:59:23Z andrew.kuchling $ | ||
| 5 | # | ||
| 6 | # I'm dreaming of an ascii character-based monochrome Christmas, | ||
| 7 | # Just like the ones I used to know! | ||
| 8 | # Via a full duplex communications channel, | ||
| 9 | # At 9600 bits per second, | ||
| 10 | # Even though it's kinda slow. | ||
| 11 | # | ||
| 12 | # I'm dreaming of an ascii character-based monochrome Christmas, | ||
| 13 | # With ev'ry C program I write! | ||
| 14 | # May your screen be merry and bright! | ||
| 15 | # And may all your Christmases be amber or green, | ||
| 16 | # (for reduced eyestrain and improved visibility)! | ||
| 17 | # | ||
| 18 | # | ||
| 19 | # Notes on the Python version: | ||
| 20 | # I used a couple of `try...except curses.error' to get around some functions | ||
| 21 | # returning ERR. The errors come from using wrapping functions to fill | ||
| 22 | # windows to the last character cell. The C version doesn't have this problem, | ||
| 23 | # it simply ignores any return values. | ||
| 24 | # | ||
| 25 | |||
| 26 | import curses | ||
| 27 | import sys | ||
| 28 | |||
| 29 | FROMWHO = "Thomas Gellekum <tg@FreeBSD.org>" | ||
| 30 | |||
| 31 | def set_color(win, color): | ||
| 32 | if curses.has_colors(): | ||
| 33 | n = color + 1 | ||
| 34 | curses.init_pair(n, color, my_bg) | ||
| 35 | win.attroff(curses.A_COLOR) | ||
| 36 | win.attron(curses.color_pair(n)) | ||
| 37 | |||
| 38 | def unset_color(win): | ||
| 39 | if curses.has_colors(): | ||
| 40 | win.attrset(curses.color_pair(0)) | ||
| 41 | |||
| 42 | def look_out(msecs): | ||
| 43 | curses.napms(msecs) | ||
| 44 | if stdscr.getch() != -1: | ||
| 45 | curses.beep() | ||
| 46 | sys.exit(0) | ||
| 47 | |||
| 48 | def boxit(): | ||
| 49 | for y in range(0, 20): | ||
| 50 | stdscr.addch(y, 7, ord('|')) | ||
| 51 | |||
| 52 | for x in range(8, 80): | ||
| 53 | stdscr.addch(19, x, ord('_')) | ||
| 54 | |||
| 55 | for x in range(0, 80): | ||
| 56 | stdscr.addch(22, x, ord('_')) | ||
| 57 | |||
| 58 | return | ||
| 59 | |||
| 60 | def seas(): | ||
| 61 | stdscr.addch(4, 1, ord('S')) | ||
| 62 | stdscr.addch(6, 1, ord('E')) | ||
| 63 | stdscr.addch(8, 1, ord('A')) | ||
| 64 | stdscr.addch(10, 1, ord('S')) | ||
| 65 | stdscr.addch(12, 1, ord('O')) | ||
| 66 | stdscr.addch(14, 1, ord('N')) | ||
| 67 | stdscr.addch(16, 1, ord("'")) | ||
| 68 | stdscr.addch(18, 1, ord('S')) | ||
| 69 | |||
| 70 | return | ||
| 71 | |||
| 72 | def greet(): | ||
| 73 | stdscr.addch(3, 5, ord('G')) | ||
| 74 | stdscr.addch(5, 5, ord('R')) | ||
| 75 | stdscr.addch(7, 5, ord('E')) | ||
| 76 | stdscr.addch(9, 5, ord('E')) | ||
| 77 | stdscr.addch(11, 5, ord('T')) | ||
| 78 | stdscr.addch(13, 5, ord('I')) | ||
| 79 | stdscr.addch(15, 5, ord('N')) | ||
| 80 | stdscr.addch(17, 5, ord('G')) | ||
| 81 | stdscr.addch(19, 5, ord('S')) | ||
| 82 | |||
| 83 | return | ||
| 84 | |||
| 85 | def fromwho(): | ||
| 86 | stdscr.addstr(21, 13, FROMWHO) | ||
| 87 | return | ||
| 88 | |||
| 89 | def tree(): | ||
| 90 | set_color(treescrn, curses.COLOR_GREEN) | ||
| 91 | treescrn.addch(1, 11, ord('/')) | ||
| 92 | treescrn.addch(2, 11, ord('/')) | ||
| 93 | treescrn.addch(3, 10, ord('/')) | ||
| 94 | treescrn.addch(4, 9, ord('/')) | ||
| 95 | treescrn.addch(5, 9, ord('/')) | ||
| 96 | treescrn.addch(6, 8, ord('/')) | ||
| 97 | treescrn.addch(7, 7, ord('/')) | ||
| 98 | treescrn.addch(8, 6, ord('/')) | ||
| 99 | treescrn.addch(9, 6, ord('/')) | ||
| 100 | treescrn.addch(10, 5, ord('/')) | ||
| 101 | treescrn.addch(11, 3, ord('/')) | ||
| 102 | treescrn.addch(12, 2, ord('/')) | ||
| 103 | |||
| 104 | treescrn.addch(1, 13, ord('\\')) | ||
| 105 | treescrn.addch(2, 13, ord('\\')) | ||
| 106 | treescrn.addch(3, 14, ord('\\')) | ||
| 107 | treescrn.addch(4, 15, ord('\\')) | ||
| 108 | treescrn.addch(5, 15, ord('\\')) | ||
| 109 | treescrn.addch(6, 16, ord('\\')) | ||
| 110 | treescrn.addch(7, 17, ord('\\')) | ||
| 111 | treescrn.addch(8, 18, ord('\\')) | ||
| 112 | treescrn.addch(9, 18, ord('\\')) | ||
| 113 | treescrn.addch(10, 19, ord('\\')) | ||
| 114 | treescrn.addch(11, 21, ord('\\')) | ||
| 115 | treescrn.addch(12, 22, ord('\\')) | ||
| 116 | |||
| 117 | treescrn.addch(4, 10, ord('_')) | ||
| 118 | treescrn.addch(4, 14, ord('_')) | ||
| 119 | treescrn.addch(8, 7, ord('_')) | ||
| 120 | treescrn.addch(8, 17, ord('_')) | ||
| 121 | |||
| 122 | treescrn.addstr(13, 0, "//////////// \\\\\\\\\\\\\\\\\\\\\\\\") | ||
| 123 | |||
| 124 | treescrn.addstr(14, 11, "| |") | ||
| 125 | treescrn.addstr(15, 11, "|_|") | ||
| 126 | |||
| 127 | unset_color(treescrn) | ||
| 128 | treescrn.refresh() | ||
| 129 | w_del_msg.refresh() | ||
| 130 | |||
| 131 | return | ||
| 132 | |||
| 133 | def balls(): | ||
| 134 | treescrn.overlay(treescrn2) | ||
| 135 | |||
| 136 | set_color(treescrn2, curses.COLOR_BLUE) | ||
| 137 | treescrn2.addch(3, 9, ord('@')) | ||
| 138 | treescrn2.addch(3, 15, ord('@')) | ||
| 139 | treescrn2.addch(4, 8, ord('@')) | ||
| 140 | treescrn2.addch(4, 16, ord('@')) | ||
| 141 | treescrn2.addch(5, 7, ord('@')) | ||
| 142 | treescrn2.addch(5, 17, ord('@')) | ||
| 143 | treescrn2.addch(7, 6, ord('@')) | ||
| 144 | treescrn2.addch(7, 18, ord('@')) | ||
| 145 | treescrn2.addch(8, 5, ord('@')) | ||
| 146 | treescrn2.addch(8, 19, ord('@')) | ||
| 147 | treescrn2.addch(10, 4, ord('@')) | ||
| 148 | treescrn2.addch(10, 20, ord('@')) | ||
| 149 | treescrn2.addch(11, 2, ord('@')) | ||
| 150 | treescrn2.addch(11, 22, ord('@')) | ||
| 151 | treescrn2.addch(12, 1, ord('@')) | ||
| 152 | treescrn2.addch(12, 23, ord('@')) | ||
| 153 | |||
| 154 | unset_color(treescrn2) | ||
| 155 | treescrn2.refresh() | ||
| 156 | w_del_msg.refresh() | ||
| 157 | return | ||
| 158 | |||
| 159 | def star(): | ||
| 160 | treescrn2.attrset(curses.A_BOLD | curses.A_BLINK) | ||
| 161 | set_color(treescrn2, curses.COLOR_YELLOW) | ||
| 162 | |||
| 163 | treescrn2.addch(0, 12, ord('*')) | ||
| 164 | treescrn2.standend() | ||
| 165 | |||
| 166 | unset_color(treescrn2) | ||
| 167 | treescrn2.refresh() | ||
| 168 | w_del_msg.refresh() | ||
| 169 | return | ||
| 170 | |||
| 171 | def strng1(): | ||
| 172 | treescrn2.attrset(curses.A_BOLD | curses.A_BLINK) | ||
| 173 | set_color(treescrn2, curses.COLOR_WHITE) | ||
| 174 | |||
| 175 | treescrn2.addch(3, 13, ord('\'')) | ||
| 176 | treescrn2.addch(3, 12, ord(':')) | ||
| 177 | treescrn2.addch(3, 11, ord('.')) | ||
| 178 | |||
| 179 | treescrn2.attroff(curses.A_BOLD | curses.A_BLINK) | ||
| 180 | unset_color(treescrn2) | ||
| 181 | |||
| 182 | treescrn2.refresh() | ||
| 183 | w_del_msg.refresh() | ||
| 184 | return | ||
| 185 | |||
| 186 | def strng2(): | ||
| 187 | treescrn2.attrset(curses.A_BOLD | curses.A_BLINK) | ||
| 188 | set_color(treescrn2, curses.COLOR_WHITE) | ||
| 189 | |||
| 190 | treescrn2.addch(5, 14, ord('\'')) | ||
| 191 | treescrn2.addch(5, 13, ord(':')) | ||
| 192 | treescrn2.addch(5, 12, ord('.')) | ||
| 193 | treescrn2.addch(5, 11, ord(',')) | ||
| 194 | treescrn2.addch(6, 10, ord('\'')) | ||
| 195 | treescrn2.addch(6, 9, ord(':')) | ||
| 196 | |||
| 197 | treescrn2.attroff(curses.A_BOLD | curses.A_BLINK) | ||
| 198 | unset_color(treescrn2) | ||
| 199 | |||
| 200 | treescrn2.refresh() | ||
| 201 | w_del_msg.refresh() | ||
| 202 | return | ||
| 203 | |||
| 204 | def strng3(): | ||
| 205 | treescrn2.attrset(curses.A_BOLD | curses.A_BLINK) | ||
| 206 | set_color(treescrn2, curses.COLOR_WHITE) | ||
| 207 | |||
| 208 | treescrn2.addch(7, 16, ord('\'')) | ||
| 209 | treescrn2.addch(7, 15, ord(':')) | ||
| 210 | treescrn2.addch(7, 14, ord('.')) | ||
| 211 | treescrn2.addch(7, 13, ord(',')) | ||
| 212 | treescrn2.addch(8, 12, ord('\'')) | ||
| 213 | treescrn2.addch(8, 11, ord(':')) | ||
| 214 | treescrn2.addch(8, 10, ord('.')) | ||
| 215 | treescrn2.addch(8, 9, ord(',')) | ||
| 216 | |||
| 217 | treescrn2.attroff(curses.A_BOLD | curses.A_BLINK) | ||
| 218 | unset_color(treescrn2) | ||
| 219 | |||
| 220 | treescrn2.refresh() | ||
| 221 | w_del_msg.refresh() | ||
| 222 | return | ||
| 223 | |||
| 224 | def strng4(): | ||
| 225 | treescrn2.attrset(curses.A_BOLD | curses.A_BLINK) | ||
| 226 | set_color(treescrn2, curses.COLOR_WHITE) | ||
| 227 | |||
| 228 | treescrn2.addch(9, 17, ord('\'')) | ||
| 229 | treescrn2.addch(9, 16, ord(':')) | ||
| 230 | treescrn2.addch(9, 15, ord('.')) | ||
| 231 | treescrn2.addch(9, 14, ord(',')) | ||
| 232 | treescrn2.addch(10, 13, ord('\'')) | ||
| 233 | treescrn2.addch(10, 12, ord(':')) | ||
| 234 | treescrn2.addch(10, 11, ord('.')) | ||
| 235 | treescrn2.addch(10, 10, ord(',')) | ||
| 236 | treescrn2.addch(11, 9, ord('\'')) | ||
| 237 | treescrn2.addch(11, 8, ord(':')) | ||
| 238 | treescrn2.addch(11, 7, ord('.')) | ||
| 239 | treescrn2.addch(11, 6, ord(',')) | ||
| 240 | treescrn2.addch(12, 5, ord('\'')) | ||
| 241 | |||
| 242 | treescrn2.attroff(curses.A_BOLD | curses.A_BLINK) | ||
| 243 | unset_color(treescrn2) | ||
| 244 | |||
| 245 | treescrn2.refresh() | ||
| 246 | w_del_msg.refresh() | ||
| 247 | return | ||
| 248 | |||
| 249 | def strng5(): | ||
| 250 | treescrn2.attrset(curses.A_BOLD | curses.A_BLINK) | ||
| 251 | set_color(treescrn2, curses.COLOR_WHITE) | ||
| 252 | |||
| 253 | treescrn2.addch(11, 19, ord('\'')) | ||
| 254 | treescrn2.addch(11, 18, ord(':')) | ||
| 255 | treescrn2.addch(11, 17, ord('.')) | ||
| 256 | treescrn2.addch(11, 16, ord(',')) | ||
| 257 | treescrn2.addch(12, 15, ord('\'')) | ||
| 258 | treescrn2.addch(12, 14, ord(':')) | ||
| 259 | treescrn2.addch(12, 13, ord('.')) | ||
| 260 | treescrn2.addch(12, 12, ord(',')) | ||
| 261 | |||
| 262 | treescrn2.attroff(curses.A_BOLD | curses.A_BLINK) | ||
| 263 | unset_color(treescrn2) | ||
| 264 | |||
| 265 | # save a fully lit tree | ||
| 266 | treescrn2.overlay(treescrn) | ||
| 267 | |||
| 268 | treescrn2.refresh() | ||
| 269 | w_del_msg.refresh() | ||
| 270 | return | ||
| 271 | |||
| 272 | def blinkit(): | ||
| 273 | treescrn8.touchwin() | ||
| 274 | |||
| 275 | for cycle in range(5): | ||
| 276 | if cycle == 0: | ||
| 277 | treescrn3.overlay(treescrn8) | ||
| 278 | treescrn8.refresh() | ||
| 279 | w_del_msg.refresh() | ||
| 280 | break | ||
| 281 | elif cycle == 1: | ||
| 282 | treescrn4.overlay(treescrn8) | ||
| 283 | treescrn8.refresh() | ||
| 284 | w_del_msg.refresh() | ||
| 285 | break | ||
| 286 | elif cycle == 2: | ||
| 287 | treescrn5.overlay(treescrn8) | ||
| 288 | treescrn8.refresh() | ||
| 289 | w_del_msg.refresh() | ||
| 290 | break | ||
| 291 | elif cycle == 3: | ||
| 292 | treescrn6.overlay(treescrn8) | ||
| 293 | treescrn8.refresh() | ||
| 294 | w_del_msg.refresh() | ||
| 295 | break | ||
| 296 | elif cycle == 4: | ||
| 297 | treescrn7.overlay(treescrn8) | ||
| 298 | treescrn8.refresh() | ||
| 299 | w_del_msg.refresh() | ||
| 300 | break | ||
| 301 | |||
| 302 | treescrn8.touchwin() | ||
| 303 | |||
| 304 | # ALL ON | ||
| 305 | treescrn.overlay(treescrn8) | ||
| 306 | treescrn8.refresh() | ||
| 307 | w_del_msg.refresh() | ||
| 308 | |||
| 309 | return | ||
| 310 | |||
| 311 | def deer_step(win, y, x): | ||
| 312 | win.mvwin(y, x) | ||
| 313 | win.refresh() | ||
| 314 | w_del_msg.refresh() | ||
| 315 | look_out(5) | ||
| 316 | |||
| 317 | def reindeer(): | ||
| 318 | y_pos = 0 | ||
| 319 | |||
| 320 | for x_pos in range(70, 62, -1): | ||
| 321 | if x_pos < 66: y_pos = 1 | ||
| 322 | for looper in range(0, 4): | ||
| 323 | dotdeer0.addch(y_pos, x_pos, ord('.')) | ||
| 324 | dotdeer0.refresh() | ||
| 325 | w_del_msg.refresh() | ||
| 326 | dotdeer0.erase() | ||
| 327 | dotdeer0.refresh() | ||
| 328 | w_del_msg.refresh() | ||
| 329 | look_out(50) | ||
| 330 | |||
| 331 | y_pos = 2 | ||
| 332 | |||
| 333 | for x_pos in range(x_pos - 1, 50, -1): | ||
| 334 | for looper in range(0, 4): | ||
| 335 | if x_pos < 56: | ||
| 336 | y_pos = 3 | ||
| 337 | |||
| 338 | try: | ||
| 339 | stardeer0.addch(y_pos, x_pos, ord('*')) | ||
| 340 | except curses.error: | ||
| 341 | pass | ||
| 342 | stardeer0.refresh() | ||
| 343 | w_del_msg.refresh() | ||
| 344 | stardeer0.erase() | ||
| 345 | stardeer0.refresh() | ||
| 346 | w_del_msg.refresh() | ||
| 347 | else: | ||
| 348 | dotdeer0.addch(y_pos, x_pos, ord('*')) | ||
| 349 | dotdeer0.refresh() | ||
| 350 | w_del_msg.refresh() | ||
| 351 | dotdeer0.erase() | ||
| 352 | dotdeer0.refresh() | ||
| 353 | w_del_msg.refresh() | ||
| 354 | |||
| 355 | x_pos = 58 | ||
| 356 | |||
| 357 | for y_pos in range(2, 5): | ||
| 358 | lildeer0.touchwin() | ||
| 359 | lildeer0.refresh() | ||
| 360 | w_del_msg.refresh() | ||
| 361 | |||
| 362 | for looper in range(0, 4): | ||
| 363 | deer_step(lildeer3, y_pos, x_pos) | ||
| 364 | deer_step(lildeer2, y_pos, x_pos) | ||
| 365 | deer_step(lildeer1, y_pos, x_pos) | ||
| 366 | deer_step(lildeer2, y_pos, x_pos) | ||
| 367 | deer_step(lildeer3, y_pos, x_pos) | ||
| 368 | |||
| 369 | lildeer0.touchwin() | ||
| 370 | lildeer0.refresh() | ||
| 371 | w_del_msg.refresh() | ||
| 372 | |||
| 373 | x_pos -= 2 | ||
| 374 | |||
| 375 | x_pos = 35 | ||
| 376 | |||
| 377 | for y_pos in range(5, 10): | ||
| 378 | |||
| 379 | middeer0.touchwin() | ||
| 380 | middeer0.refresh() | ||
| 381 | w_del_msg.refresh() | ||
| 382 | |||
| 383 | for looper in range(2): | ||
| 384 | deer_step(middeer3, y_pos, x_pos) | ||
| 385 | deer_step(middeer2, y_pos, x_pos) | ||
| 386 | deer_step(middeer1, y_pos, x_pos) | ||
| 387 | deer_step(middeer2, y_pos, x_pos) | ||
| 388 | deer_step(middeer3, y_pos, x_pos) | ||
| 389 | |||
| 390 | middeer0.touchwin() | ||
| 391 | middeer0.refresh() | ||
| 392 | w_del_msg.refresh() | ||
| 393 | |||
| 394 | x_pos -= 3 | ||
| 395 | |||
| 396 | look_out(300) | ||
| 397 | |||
| 398 | y_pos = 1 | ||
| 399 | |||
| 400 | for x_pos in range(8, 16): | ||
| 401 | deer_step(bigdeer4, y_pos, x_pos) | ||
| 402 | deer_step(bigdeer3, y_pos, x_pos) | ||
| 403 | deer_step(bigdeer2, y_pos, x_pos) | ||
| 404 | deer_step(bigdeer1, y_pos, x_pos) | ||
| 405 | deer_step(bigdeer2, y_pos, x_pos) | ||
| 406 | deer_step(bigdeer3, y_pos, x_pos) | ||
| 407 | deer_step(bigdeer4, y_pos, x_pos) | ||
| 408 | deer_step(bigdeer0, y_pos, x_pos) | ||
| 409 | |||
| 410 | x_pos -= 1 | ||
| 411 | |||
| 412 | for looper in range(0, 6): | ||
| 413 | deer_step(lookdeer4, y_pos, x_pos) | ||
| 414 | deer_step(lookdeer3, y_pos, x_pos) | ||
| 415 | deer_step(lookdeer2, y_pos, x_pos) | ||
| 416 | deer_step(lookdeer1, y_pos, x_pos) | ||
| 417 | deer_step(lookdeer2, y_pos, x_pos) | ||
| 418 | deer_step(lookdeer3, y_pos, x_pos) | ||
| 419 | deer_step(lookdeer4, y_pos, x_pos) | ||
| 420 | |||
| 421 | deer_step(lookdeer0, y_pos, x_pos) | ||
| 422 | |||
| 423 | for y_pos in range(y_pos, 10): | ||
| 424 | for looper in range(0, 2): | ||
| 425 | deer_step(bigdeer4, y_pos, x_pos) | ||
| 426 | deer_step(bigdeer3, y_pos, x_pos) | ||
| 427 | deer_step(bigdeer2, y_pos, x_pos) | ||
| 428 | deer_step(bigdeer1, y_pos, x_pos) | ||
| 429 | deer_step(bigdeer2, y_pos, x_pos) | ||
| 430 | deer_step(bigdeer3, y_pos, x_pos) | ||
| 431 | deer_step(bigdeer4, y_pos, x_pos) | ||
| 432 | deer_step(bigdeer0, y_pos, x_pos) | ||
| 433 | |||
| 434 | y_pos -= 1 | ||
| 435 | |||
| 436 | deer_step(lookdeer3, y_pos, x_pos) | ||
| 437 | return | ||
| 438 | |||
| 439 | def main(win): | ||
| 440 | global stdscr | ||
| 441 | stdscr = win | ||
| 442 | |||
| 443 | global my_bg, y_pos, x_pos | ||
| 444 | global treescrn, treescrn2, treescrn3, treescrn4 | ||
| 445 | global treescrn5, treescrn6, treescrn7, treescrn8 | ||
| 446 | global dotdeer0, stardeer0 | ||
| 447 | global lildeer0, lildeer1, lildeer2, lildeer3 | ||
| 448 | global middeer0, middeer1, middeer2, middeer3 | ||
| 449 | global bigdeer0, bigdeer1, bigdeer2, bigdeer3, bigdeer4 | ||
| 450 | global lookdeer0, lookdeer1, lookdeer2, lookdeer3, lookdeer4 | ||
| 451 | global w_holiday, w_del_msg | ||
| 452 | |||
| 453 | my_bg = curses.COLOR_BLACK | ||
| 454 | # curses.curs_set(0) | ||
| 455 | |||
| 456 | treescrn = curses.newwin(16, 27, 3, 53) | ||
| 457 | treescrn2 = curses.newwin(16, 27, 3, 53) | ||
| 458 | treescrn3 = curses.newwin(16, 27, 3, 53) | ||
| 459 | treescrn4 = curses.newwin(16, 27, 3, 53) | ||
| 460 | treescrn5 = curses.newwin(16, 27, 3, 53) | ||
| 461 | treescrn6 = curses.newwin(16, 27, 3, 53) | ||
| 462 | treescrn7 = curses.newwin(16, 27, 3, 53) | ||
| 463 | treescrn8 = curses.newwin(16, 27, 3, 53) | ||
| 464 | |||
| 465 | dotdeer0 = curses.newwin(3, 71, 0, 8) | ||
| 466 | |||
| 467 | stardeer0 = curses.newwin(4, 56, 0, 8) | ||
| 468 | |||
| 469 | lildeer0 = curses.newwin(7, 53, 0, 8) | ||
| 470 | lildeer1 = curses.newwin(2, 4, 0, 0) | ||
| 471 | lildeer2 = curses.newwin(2, 4, 0, 0) | ||
| 472 | lildeer3 = curses.newwin(2, 4, 0, 0) | ||
| 473 | |||
| 474 | middeer0 = curses.newwin(15, 42, 0, 8) | ||
| 475 | middeer1 = curses.newwin(3, 7, 0, 0) | ||
| 476 | middeer2 = curses.newwin(3, 7, 0, 0) | ||
| 477 | middeer3 = curses.newwin(3, 7, 0, 0) | ||
| 478 | |||
| 479 | bigdeer0 = curses.newwin(10, 23, 0, 0) | ||
| 480 | bigdeer1 = curses.newwin(10, 23, 0, 0) | ||
| 481 | bigdeer2 = curses.newwin(10, 23, 0, 0) | ||
| 482 | bigdeer3 = curses.newwin(10, 23, 0, 0) | ||
| 483 | bigdeer4 = curses.newwin(10, 23, 0, 0) | ||
| 484 | |||
| 485 | lookdeer0 = curses.newwin(10, 25, 0, 0) | ||
| 486 | lookdeer1 = curses.newwin(10, 25, 0, 0) | ||
| 487 | lookdeer2 = curses.newwin(10, 25, 0, 0) | ||
| 488 | lookdeer3 = curses.newwin(10, 25, 0, 0) | ||
| 489 | lookdeer4 = curses.newwin(10, 25, 0, 0) | ||
| 490 | |||
| 491 | w_holiday = curses.newwin(1, 27, 3, 27) | ||
| 492 | |||
| 493 | w_del_msg = curses.newwin(1, 20, 23, 60) | ||
| 494 | |||
| 495 | try: | ||
| 496 | w_del_msg.addstr(0, 0, "Hit any key to quit") | ||
| 497 | except curses.error: | ||
| 498 | pass | ||
| 499 | |||
| 500 | try: | ||
| 501 | w_holiday.addstr(0, 0, "H A P P Y H O L I D A Y S") | ||
| 502 | except curses.error: | ||
| 503 | pass | ||
| 504 | |||
| 505 | # set up the windows for our various reindeer | ||
| 506 | lildeer1.addch(0, 0, ord('V')) | ||
| 507 | lildeer1.addch(1, 0, ord('@')) | ||
| 508 | lildeer1.addch(1, 1, ord('<')) | ||
| 509 | lildeer1.addch(1, 2, ord('>')) | ||
| 510 | try: | ||
| 511 | lildeer1.addch(1, 3, ord('~')) | ||
| 512 | except curses.error: | ||
| 513 | pass | ||
| 514 | |||
| 515 | lildeer2.addch(0, 0, ord('V')) | ||
| 516 | lildeer2.addch(1, 0, ord('@')) | ||
| 517 | lildeer2.addch(1, 1, ord('|')) | ||
| 518 | lildeer2.addch(1, 2, ord('|')) | ||
| 519 | try: | ||
| 520 | lildeer2.addch(1, 3, ord('~')) | ||
| 521 | except curses.error: | ||
| 522 | pass | ||
| 523 | |||
| 524 | lildeer3.addch(0, 0, ord('V')) | ||
| 525 | lildeer3.addch(1, 0, ord('@')) | ||
| 526 | lildeer3.addch(1, 1, ord('>')) | ||
| 527 | lildeer3.addch(1, 2, ord('<')) | ||
| 528 | try: | ||
| 529 | lildeer2.addch(1, 3, ord('~')) # XXX | ||
| 530 | except curses.error: | ||
| 531 | pass | ||
| 532 | |||
| 533 | middeer1.addch(0, 2, ord('y')) | ||
| 534 | middeer1.addch(0, 3, ord('y')) | ||
| 535 | middeer1.addch(1, 2, ord('0')) | ||
| 536 | middeer1.addch(1, 3, ord('(')) | ||
| 537 | middeer1.addch(1, 4, ord('=')) | ||
| 538 | middeer1.addch(1, 5, ord(')')) | ||
| 539 | middeer1.addch(1, 6, ord('~')) | ||
| 540 | middeer1.addch(2, 3, ord('\\')) | ||
| 541 | middeer1.addch(2, 5, ord('/')) | ||
| 542 | |||
| 543 | middeer2.addch(0, 2, ord('y')) | ||
| 544 | middeer2.addch(0, 3, ord('y')) | ||
| 545 | middeer2.addch(1, 2, ord('0')) | ||
| 546 | middeer2.addch(1, 3, ord('(')) | ||
| 547 | middeer2.addch(1, 4, ord('=')) | ||
| 548 | middeer2.addch(1, 5, ord(')')) | ||
| 549 | middeer2.addch(1, 6, ord('~')) | ||
| 550 | middeer2.addch(2, 3, ord('|')) | ||
| 551 | middeer2.addch(2, 5, ord('|')) | ||
| 552 | |||
| 553 | middeer3.addch(0, 2, ord('y')) | ||
| 554 | middeer3.addch(0, 3, ord('y')) | ||
| 555 | middeer3.addch(1, 2, ord('0')) | ||
| 556 | middeer3.addch(1, 3, ord('(')) | ||
| 557 | middeer3.addch(1, 4, ord('=')) | ||
| 558 | middeer3.addch(1, 5, ord(')')) | ||
| 559 | middeer3.addch(1, 6, ord('~')) | ||
| 560 | middeer3.addch(2, 3, ord('/')) | ||
| 561 | middeer3.addch(2, 5, ord('\\')) | ||
| 562 | |||
| 563 | bigdeer1.addch(0, 17, ord('\\')) | ||
| 564 | bigdeer1.addch(0, 18, ord('/')) | ||
| 565 | bigdeer1.addch(0, 19, ord('\\')) | ||
| 566 | bigdeer1.addch(0, 20, ord('/')) | ||
| 567 | bigdeer1.addch(1, 18, ord('\\')) | ||
| 568 | bigdeer1.addch(1, 20, ord('/')) | ||
| 569 | bigdeer1.addch(2, 19, ord('|')) | ||
| 570 | bigdeer1.addch(2, 20, ord('_')) | ||
| 571 | bigdeer1.addch(3, 18, ord('/')) | ||
| 572 | bigdeer1.addch(3, 19, ord('^')) | ||
| 573 | bigdeer1.addch(3, 20, ord('0')) | ||
| 574 | bigdeer1.addch(3, 21, ord('\\')) | ||
| 575 | bigdeer1.addch(4, 17, ord('/')) | ||
| 576 | bigdeer1.addch(4, 18, ord('/')) | ||
| 577 | bigdeer1.addch(4, 19, ord('\\')) | ||
| 578 | bigdeer1.addch(4, 22, ord('\\')) | ||
| 579 | bigdeer1.addstr(5, 7, "^~~~~~~~~// ~~U") | ||
| 580 | bigdeer1.addstr(6, 7, "( \\_____( /") # )) | ||
| 581 | bigdeer1.addstr(7, 8, "( ) /") | ||
| 582 | bigdeer1.addstr(8, 9, "\\\\ /") | ||
| 583 | bigdeer1.addstr(9, 11, "\\>/>") | ||
| 584 | |||
| 585 | bigdeer2.addch(0, 17, ord('\\')) | ||
| 586 | bigdeer2.addch(0, 18, ord('/')) | ||
| 587 | bigdeer2.addch(0, 19, ord('\\')) | ||
| 588 | bigdeer2.addch(0, 20, ord('/')) | ||
| 589 | bigdeer2.addch(1, 18, ord('\\')) | ||
| 590 | bigdeer2.addch(1, 20, ord('/')) | ||
| 591 | bigdeer2.addch(2, 19, ord('|')) | ||
| 592 | bigdeer2.addch(2, 20, ord('_')) | ||
| 593 | bigdeer2.addch(3, 18, ord('/')) | ||
| 594 | bigdeer2.addch(3, 19, ord('^')) | ||
| 595 | bigdeer2.addch(3, 20, ord('0')) | ||
| 596 | bigdeer2.addch(3, 21, ord('\\')) | ||
| 597 | bigdeer2.addch(4, 17, ord('/')) | ||
| 598 | bigdeer2.addch(4, 18, ord('/')) | ||
| 599 | bigdeer2.addch(4, 19, ord('\\')) | ||
| 600 | bigdeer2.addch(4, 22, ord('\\')) | ||
| 601 | bigdeer2.addstr(5, 7, "^~~~~~~~~// ~~U") | ||
| 602 | bigdeer2.addstr(6, 7, "(( )____( /") # )) | ||
| 603 | bigdeer2.addstr(7, 7, "( / |") | ||
| 604 | bigdeer2.addstr(8, 8, "\\/ |") | ||
| 605 | bigdeer2.addstr(9, 9, "|> |>") | ||
| 606 | |||
| 607 | bigdeer3.addch(0, 17, ord('\\')) | ||
| 608 | bigdeer3.addch(0, 18, ord('/')) | ||
| 609 | bigdeer3.addch(0, 19, ord('\\')) | ||
| 610 | bigdeer3.addch(0, 20, ord('/')) | ||
| 611 | bigdeer3.addch(1, 18, ord('\\')) | ||
| 612 | bigdeer3.addch(1, 20, ord('/')) | ||
| 613 | bigdeer3.addch(2, 19, ord('|')) | ||
| 614 | bigdeer3.addch(2, 20, ord('_')) | ||
| 615 | bigdeer3.addch(3, 18, ord('/')) | ||
| 616 | bigdeer3.addch(3, 19, ord('^')) | ||
| 617 | bigdeer3.addch(3, 20, ord('0')) | ||
| 618 | bigdeer3.addch(3, 21, ord('\\')) | ||
| 619 | bigdeer3.addch(4, 17, ord('/')) | ||
| 620 | bigdeer3.addch(4, 18, ord('/')) | ||
| 621 | bigdeer3.addch(4, 19, ord('\\')) | ||
| 622 | bigdeer3.addch(4, 22, ord('\\')) | ||
| 623 | bigdeer3.addstr(5, 7, "^~~~~~~~~// ~~U") | ||
| 624 | bigdeer3.addstr(6, 6, "( ()_____( /") # )) | ||
| 625 | bigdeer3.addstr(7, 6, "/ / /") | ||
| 626 | bigdeer3.addstr(8, 5, "|/ \\") | ||
| 627 | bigdeer3.addstr(9, 5, "/> \\>") | ||
| 628 | |||
| 629 | bigdeer4.addch(0, 17, ord('\\')) | ||
| 630 | bigdeer4.addch(0, 18, ord('/')) | ||
| 631 | bigdeer4.addch(0, 19, ord('\\')) | ||
| 632 | bigdeer4.addch(0, 20, ord('/')) | ||
| 633 | bigdeer4.addch(1, 18, ord('\\')) | ||
| 634 | bigdeer4.addch(1, 20, ord('/')) | ||
| 635 | bigdeer4.addch(2, 19, ord('|')) | ||
| 636 | bigdeer4.addch(2, 20, ord('_')) | ||
| 637 | bigdeer4.addch(3, 18, ord('/')) | ||
| 638 | bigdeer4.addch(3, 19, ord('^')) | ||
| 639 | bigdeer4.addch(3, 20, ord('0')) | ||
| 640 | bigdeer4.addch(3, 21, ord('\\')) | ||
| 641 | bigdeer4.addch(4, 17, ord('/')) | ||
| 642 | bigdeer4.addch(4, 18, ord('/')) | ||
| 643 | bigdeer4.addch(4, 19, ord('\\')) | ||
| 644 | bigdeer4.addch(4, 22, ord('\\')) | ||
| 645 | bigdeer4.addstr(5, 7, "^~~~~~~~~// ~~U") | ||
| 646 | bigdeer4.addstr(6, 6, "( )______( /") # ) | ||
| 647 | bigdeer4.addstr(7, 5, "(/ \\") # ) | ||
| 648 | bigdeer4.addstr(8, 0, "v___= ----^") | ||
| 649 | |||
| 650 | lookdeer1.addstr(0, 16, "\\/ \\/") | ||
| 651 | lookdeer1.addstr(1, 17, "\\Y/ \\Y/") | ||
| 652 | lookdeer1.addstr(2, 19, "\\=/") | ||
| 653 | lookdeer1.addstr(3, 17, "^\\o o/^") | ||
| 654 | lookdeer1.addstr(4, 17, "//( )") | ||
| 655 | lookdeer1.addstr(5, 7, "^~~~~~~~~// \\O/") | ||
| 656 | lookdeer1.addstr(6, 7, "( \\_____( /") # )) | ||
| 657 | lookdeer1.addstr(7, 8, "( ) /") | ||
| 658 | lookdeer1.addstr(8, 9, "\\\\ /") | ||
| 659 | lookdeer1.addstr(9, 11, "\\>/>") | ||
| 660 | |||
| 661 | lookdeer2.addstr(0, 16, "\\/ \\/") | ||
| 662 | lookdeer2.addstr(1, 17, "\\Y/ \\Y/") | ||
| 663 | lookdeer2.addstr(2, 19, "\\=/") | ||
| 664 | lookdeer2.addstr(3, 17, "^\\o o/^") | ||
| 665 | lookdeer2.addstr(4, 17, "//( )") | ||
| 666 | lookdeer2.addstr(5, 7, "^~~~~~~~~// \\O/") | ||
| 667 | lookdeer2.addstr(6, 7, "(( )____( /") # )) | ||
| 668 | lookdeer2.addstr(7, 7, "( / |") | ||
| 669 | lookdeer2.addstr(8, 8, "\\/ |") | ||
| 670 | lookdeer2.addstr(9, 9, "|> |>") | ||
| 671 | |||
| 672 | lookdeer3.addstr(0, 16, "\\/ \\/") | ||
| 673 | lookdeer3.addstr(1, 17, "\\Y/ \\Y/") | ||
| 674 | lookdeer3.addstr(2, 19, "\\=/") | ||
| 675 | lookdeer3.addstr(3, 17, "^\\o o/^") | ||
| 676 | lookdeer3.addstr(4, 17, "//( )") | ||
| 677 | lookdeer3.addstr(5, 7, "^~~~~~~~~// \\O/") | ||
| 678 | lookdeer3.addstr(6, 6, "( ()_____( /") # )) | ||
| 679 | lookdeer3.addstr(7, 6, "/ / /") | ||
| 680 | lookdeer3.addstr(8, 5, "|/ \\") | ||
| 681 | lookdeer3.addstr(9, 5, "/> \\>") | ||
| 682 | |||
| 683 | lookdeer4.addstr(0, 16, "\\/ \\/") | ||
| 684 | lookdeer4.addstr(1, 17, "\\Y/ \\Y/") | ||
| 685 | lookdeer4.addstr(2, 19, "\\=/") | ||
| 686 | lookdeer4.addstr(3, 17, "^\\o o/^") | ||
| 687 | lookdeer4.addstr(4, 17, "//( )") | ||
| 688 | lookdeer4.addstr(5, 7, "^~~~~~~~~// \\O/") | ||
| 689 | lookdeer4.addstr(6, 6, "( )______( /") # ) | ||
| 690 | lookdeer4.addstr(7, 5, "(/ \\") # ) | ||
| 691 | lookdeer4.addstr(8, 0, "v___= ----^") | ||
| 692 | |||
| 693 | ############################################### | ||
| 694 | curses.cbreak() | ||
| 695 | stdscr.nodelay(1) | ||
| 696 | |||
| 697 | while 1: | ||
| 698 | stdscr.clear() | ||
| 699 | treescrn.erase() | ||
| 700 | w_del_msg.touchwin() | ||
| 701 | treescrn.touchwin() | ||
| 702 | treescrn2.erase() | ||
| 703 | treescrn2.touchwin() | ||
| 704 | treescrn8.erase() | ||
| 705 | treescrn8.touchwin() | ||
| 706 | stdscr.refresh() | ||
| 707 | look_out(150) | ||
| 708 | boxit() | ||
| 709 | stdscr.refresh() | ||
| 710 | look_out(150) | ||
| 711 | seas() | ||
| 712 | stdscr.refresh() | ||
| 713 | greet() | ||
| 714 | stdscr.refresh() | ||
| 715 | look_out(150) | ||
| 716 | fromwho() | ||
| 717 | stdscr.refresh() | ||
| 718 | look_out(150) | ||
| 719 | tree() | ||
| 720 | look_out(150) | ||
| 721 | balls() | ||
| 722 | look_out(150) | ||
| 723 | star() | ||
| 724 | look_out(150) | ||
| 725 | strng1() | ||
| 726 | strng2() | ||
| 727 | strng3() | ||
| 728 | strng4() | ||
| 729 | strng5() | ||
| 730 | |||
| 731 | # set up the windows for our blinking trees | ||
| 732 | # | ||
| 733 | # treescrn3 | ||
| 734 | treescrn.overlay(treescrn3) | ||
| 735 | |||
| 736 | # balls | ||
| 737 | treescrn3.addch(4, 18, ord(' ')) | ||
| 738 | treescrn3.addch(7, 6, ord(' ')) | ||
| 739 | treescrn3.addch(8, 19, ord(' ')) | ||
| 740 | treescrn3.addch(11, 22, ord(' ')) | ||
| 741 | |||
| 742 | # star | ||
| 743 | treescrn3.addch(0, 12, ord('*')) | ||
| 744 | |||
| 745 | # strng1 | ||
| 746 | treescrn3.addch(3, 11, ord(' ')) | ||
| 747 | |||
| 748 | # strng2 | ||
| 749 | treescrn3.addch(5, 13, ord(' ')) | ||
| 750 | treescrn3.addch(6, 10, ord(' ')) | ||
| 751 | |||
| 752 | # strng3 | ||
| 753 | treescrn3.addch(7, 16, ord(' ')) | ||
| 754 | treescrn3.addch(7, 14, ord(' ')) | ||
| 755 | |||
| 756 | # strng4 | ||
| 757 | treescrn3.addch(10, 13, ord(' ')) | ||
| 758 | treescrn3.addch(10, 10, ord(' ')) | ||
| 759 | treescrn3.addch(11, 8, ord(' ')) | ||
| 760 | |||
| 761 | # strng5 | ||
| 762 | treescrn3.addch(11, 18, ord(' ')) | ||
| 763 | treescrn3.addch(12, 13, ord(' ')) | ||
| 764 | |||
| 765 | # treescrn4 | ||
| 766 | treescrn.overlay(treescrn4) | ||
| 767 | |||
| 768 | # balls | ||
| 769 | treescrn4.addch(3, 9, ord(' ')) | ||
| 770 | treescrn4.addch(4, 16, ord(' ')) | ||
| 771 | treescrn4.addch(7, 6, ord(' ')) | ||
| 772 | treescrn4.addch(8, 19, ord(' ')) | ||
| 773 | treescrn4.addch(11, 2, ord(' ')) | ||
| 774 | treescrn4.addch(12, 23, ord(' ')) | ||
| 775 | |||
| 776 | # star | ||
| 777 | treescrn4.standout() | ||
| 778 | treescrn4.addch(0, 12, ord('*')) | ||
| 779 | treescrn4.standend() | ||
| 780 | |||
| 781 | # strng1 | ||
| 782 | treescrn4.addch(3, 13, ord(' ')) | ||
| 783 | |||
| 784 | # strng2 | ||
| 785 | |||
| 786 | # strng3 | ||
| 787 | treescrn4.addch(7, 15, ord(' ')) | ||
| 788 | treescrn4.addch(8, 11, ord(' ')) | ||
| 789 | |||
| 790 | # strng4 | ||
| 791 | treescrn4.addch(9, 16, ord(' ')) | ||
| 792 | treescrn4.addch(10, 12, ord(' ')) | ||
| 793 | treescrn4.addch(11, 8, ord(' ')) | ||
| 794 | |||
| 795 | # strng5 | ||
| 796 | treescrn4.addch(11, 18, ord(' ')) | ||
| 797 | treescrn4.addch(12, 14, ord(' ')) | ||
| 798 | |||
| 799 | # treescrn5 | ||
| 800 | treescrn.overlay(treescrn5) | ||
| 801 | |||
| 802 | # balls | ||
| 803 | treescrn5.addch(3, 15, ord(' ')) | ||
| 804 | treescrn5.addch(10, 20, ord(' ')) | ||
| 805 | treescrn5.addch(12, 1, ord(' ')) | ||
| 806 | |||
| 807 | # star | ||
| 808 | treescrn5.addch(0, 12, ord(' ')) | ||
| 809 | |||
| 810 | # strng1 | ||
| 811 | treescrn5.addch(3, 11, ord(' ')) | ||
| 812 | |||
| 813 | # strng2 | ||
| 814 | treescrn5.addch(5, 12, ord(' ')) | ||
| 815 | |||
| 816 | # strng3 | ||
| 817 | treescrn5.addch(7, 14, ord(' ')) | ||
| 818 | treescrn5.addch(8, 10, ord(' ')) | ||
| 819 | |||
| 820 | # strng4 | ||
| 821 | treescrn5.addch(9, 15, ord(' ')) | ||
| 822 | treescrn5.addch(10, 11, ord(' ')) | ||
| 823 | treescrn5.addch(11, 7, ord(' ')) | ||
| 824 | |||
| 825 | # strng5 | ||
| 826 | treescrn5.addch(11, 17, ord(' ')) | ||
| 827 | treescrn5.addch(12, 13, ord(' ')) | ||
| 828 | |||
| 829 | # treescrn6 | ||
| 830 | treescrn.overlay(treescrn6) | ||
| 831 | |||
| 832 | # balls | ||
| 833 | treescrn6.addch(6, 7, ord(' ')) | ||
| 834 | treescrn6.addch(7, 18, ord(' ')) | ||
| 835 | treescrn6.addch(10, 4, ord(' ')) | ||
| 836 | treescrn6.addch(11, 23, ord(' ')) | ||
| 837 | |||
| 838 | # star | ||
| 839 | treescrn6.standout() | ||
| 840 | treescrn6.addch(0, 12, ord('*')) | ||
| 841 | treescrn6.standend() | ||
| 842 | |||
| 843 | # strng1 | ||
| 844 | |||
| 845 | # strng2 | ||
| 846 | treescrn6.addch(5, 11, ord(' ')) | ||
| 847 | |||
| 848 | # strng3 | ||
| 849 | treescrn6.addch(7, 13, ord(' ')) | ||
| 850 | treescrn6.addch(8, 9, ord(' ')) | ||
| 851 | |||
| 852 | # strng4 | ||
| 853 | treescrn6.addch(9, 14, ord(' ')) | ||
| 854 | treescrn6.addch(10, 10, ord(' ')) | ||
| 855 | treescrn6.addch(11, 6, ord(' ')) | ||
| 856 | |||
| 857 | # strng5 | ||
| 858 | treescrn6.addch(11, 16, ord(' ')) | ||
| 859 | treescrn6.addch(12, 12, ord(' ')) | ||
| 860 | |||
| 861 | # treescrn7 | ||
| 862 | |||
| 863 | treescrn.overlay(treescrn7) | ||
| 864 | |||
| 865 | # balls | ||
| 866 | treescrn7.addch(3, 15, ord(' ')) | ||
| 867 | treescrn7.addch(6, 7, ord(' ')) | ||
| 868 | treescrn7.addch(7, 18, ord(' ')) | ||
| 869 | treescrn7.addch(10, 4, ord(' ')) | ||
| 870 | treescrn7.addch(11, 22, ord(' ')) | ||
| 871 | |||
| 872 | # star | ||
| 873 | treescrn7.addch(0, 12, ord('*')) | ||
| 874 | |||
| 875 | # strng1 | ||
| 876 | treescrn7.addch(3, 12, ord(' ')) | ||
| 877 | |||
| 878 | # strng2 | ||
| 879 | treescrn7.addch(5, 13, ord(' ')) | ||
| 880 | treescrn7.addch(6, 9, ord(' ')) | ||
| 881 | |||
| 882 | # strng3 | ||
| 883 | treescrn7.addch(7, 15, ord(' ')) | ||
| 884 | treescrn7.addch(8, 11, ord(' ')) | ||
| 885 | |||
| 886 | # strng4 | ||
| 887 | treescrn7.addch(9, 16, ord(' ')) | ||
| 888 | treescrn7.addch(10, 12, ord(' ')) | ||
| 889 | treescrn7.addch(11, 8, ord(' ')) | ||
| 890 | |||
| 891 | # strng5 | ||
| 892 | treescrn7.addch(11, 18, ord(' ')) | ||
| 893 | treescrn7.addch(12, 14, ord(' ')) | ||
| 894 | |||
| 895 | look_out(150) | ||
| 896 | reindeer() | ||
| 897 | |||
| 898 | w_holiday.touchwin() | ||
| 899 | w_holiday.refresh() | ||
| 900 | w_del_msg.refresh() | ||
| 901 | |||
| 902 | look_out(500) | ||
| 903 | for i in range(0, 20): | ||
| 904 | blinkit() | ||
| 905 | |||
| 906 | curses.wrapper(main) | ||
| @@ -0,0 +1,57 @@ | |||
| 1 | # Makefile for embedded Python use demo. | ||
| 2 | # (This version tailored for my Red Hat Linux 6.1 setup; | ||
| 3 | # edit lines marked with XXX.) | ||
| 4 | |||
| 5 | # XXX The compiler you are using | ||
| 6 | CC= gcc | ||
| 7 | |||
| 8 | # XXX Top of the build tree and source tree | ||
| 9 | blddir= ../.. | ||
| 10 | srcdir= ../.. | ||
| 11 | |||
| 12 | # Python version | ||
| 13 | VERSION= 2.5 | ||
| 14 | |||
| 15 | # Compiler flags | ||
| 16 | OPT= -g | ||
| 17 | INCLUDES= -I$(srcdir)/Include -I$(blddir) | ||
| 18 | CFLAGS= $(OPT) | ||
| 19 | CPPFLAGS= $(INCLUDES) | ||
| 20 | |||
| 21 | # The Python library | ||
| 22 | LIBPYTHON= $(blddir)/libpython$(VERSION).a | ||
| 23 | |||
| 24 | # XXX edit LIBS (in particular) to match $(blddir)/Modules/Makefile | ||
| 25 | LIBS= -lnsl -ldl -lreadline -ltermcap -lieee -lpthread -lutil | ||
| 26 | LDFLAGS= -Xlinker -export-dynamic | ||
| 27 | SYSLIBS= -lm | ||
| 28 | MODLIBS= | ||
| 29 | ALLLIBS= $(LIBPYTHON) $(MODLIBS) $(LIBS) $(SYSLIBS) | ||
| 30 | |||
| 31 | # Build the demo applications | ||
| 32 | all: demo loop importexc | ||
| 33 | demo: demo.o | ||
| 34 | $(CC) $(LDFLAGS) demo.o $(ALLLIBS) -o demo | ||
| 35 | |||
| 36 | loop: loop.o | ||
| 37 | $(CC) $(LDFLAGS) loop.o $(ALLLIBS) -o loop | ||
| 38 | |||
| 39 | importexc: importexc.o | ||
| 40 | $(CC) $(LDFLAGS) importexc.o $(ALLLIBS) -o importexc | ||
| 41 | |||
| 42 | # Administrative targets | ||
| 43 | |||
| 44 | test: demo | ||
| 45 | ./demo | ||
| 46 | |||
| 47 | COMMAND="print 'hello world'" | ||
| 48 | looptest: loop | ||
| 49 | ./loop $(COMMAND) | ||
| 50 | |||
| 51 | clean: | ||
| 52 | -rm -f *.o core | ||
| 53 | |||
| 54 | clobber: clean | ||
| 55 | -rm -f *~ @* '#'* demo loop importexc | ||
| 56 | |||
| 57 | realclean: clobber | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | This directory show how to embed the Python interpreter in your own | ||
| 2 | application. The file demo.c shows you all that is needed in your C | ||
| 3 | code. | ||
| 4 | |||
| 5 | To build it, you may have to edit the Makefile: | ||
| 6 | |||
| 7 | 1) set blddir to the directory where you built Python, if it isn't in | ||
| 8 | the source directory (../..) | ||
| 9 | |||
| 10 | 2) change the variables that together define the list of libraries | ||
| 11 | (MODLIBS, LIBS, SYSLIBS) to link with, to match their definitions in | ||
| 12 | $(blddir)/Modules/Makefile | ||
| 13 | |||
| 14 | An additional test program, loop.c, is used to experiment with memory | ||
| 15 | leakage caused by repeated initialization and finalization of the | ||
| 16 | interpreter. It can be build by saying "make loop" and tested with | ||
| 17 | "make looptest". Command line usage is "./loop <python-command>", | ||
| 18 | e.g. "./loop 'print 2+2'" should spit out an endless number of lines | ||
| 19 | containing the number 4. | ||
| @@ -0,0 +1,65 @@ | |||
| 1 | /* Example of embedding Python in another program */ | ||
| 2 | |||
| 3 | #include "Python.h" | ||
| 4 | |||
| 5 | void initxyzzy(void); /* Forward */ | ||
| 6 | |||
| 7 | main(int argc, char **argv) | ||
| 8 | { | ||
| 9 | /* Pass argv[0] to the Python interpreter */ | ||
| 10 | Py_SetProgramName(argv[0]); | ||
| 11 | |||
| 12 | /* Initialize the Python interpreter. Required. */ | ||
| 13 | Py_Initialize(); | ||
| 14 | |||
| 15 | /* Add a static module */ | ||
| 16 | initxyzzy(); | ||
| 17 | |||
| 18 | /* Define sys.argv. It is up to the application if you | ||
| 19 | want this; you can also let it undefined (since the Python | ||
| 20 | code is generally not a main program it has no business | ||
| 21 | touching sys.argv...) */ | ||
| 22 | PySys_SetArgv(argc, argv); | ||
| 23 | |||
| 24 | /* Do some application specific code */ | ||
| 25 | printf("Hello, brave new world\n\n"); | ||
| 26 | |||
| 27 | /* Execute some Python statements (in module __main__) */ | ||
| 28 | PyRun_SimpleString("import sys\n"); | ||
| 29 | PyRun_SimpleString("print sys.builtin_module_names\n"); | ||
| 30 | PyRun_SimpleString("print sys.modules.keys()\n"); | ||
| 31 | PyRun_SimpleString("print sys.executable\n"); | ||
| 32 | PyRun_SimpleString("print sys.argv\n"); | ||
| 33 | |||
| 34 | /* Note that you can call any public function of the Python | ||
| 35 | interpreter here, e.g. call_object(). */ | ||
| 36 | |||
| 37 | /* Some more application specific code */ | ||
| 38 | printf("\nGoodbye, cruel world\n"); | ||
| 39 | |||
| 40 | /* Exit, cleaning up the interpreter */ | ||
| 41 | Py_Exit(0); | ||
| 42 | /*NOTREACHED*/ | ||
| 43 | } | ||
| 44 | |||
| 45 | /* A static module */ | ||
| 46 | |||
| 47 | /* 'self' is not used */ | ||
| 48 | static PyObject * | ||
| 49 | xyzzy_foo(PyObject *self, PyObject* args) | ||
| 50 | { | ||
| 51 | return PyInt_FromLong(42L); | ||
| 52 | } | ||
| 53 | |||
| 54 | static PyMethodDef xyzzy_methods[] = { | ||
| 55 | {"foo", xyzzy_foo, METH_NOARGS, | ||
| 56 | "Return the meaning of everything."}, | ||
| 57 | {NULL, NULL} /* sentinel */ | ||
| 58 | }; | ||
| 59 | |||
| 60 | void | ||
| 61 | initxyzzy(void) | ||
| 62 | { | ||
| 63 | PyImport_AddModule("xyzzy"); | ||
| 64 | Py_InitModule("xyzzy", xyzzy_methods); | ||
| 65 | } | ||
| @@ -0,0 +1,17 @@ | |||
| 1 | #include <Python.h> | ||
| 2 | |||
| 3 | char* cmd = "import exceptions"; | ||
| 4 | |||
| 5 | int main() | ||
| 6 | { | ||
| 7 | Py_Initialize(); | ||
| 8 | PyEval_InitThreads(); | ||
| 9 | PyRun_SimpleString(cmd); | ||
| 10 | Py_EndInterpreter(PyThreadState_Get()); | ||
| 11 | |||
| 12 | Py_NewInterpreter(); | ||
| 13 | PyRun_SimpleString(cmd); | ||
| 14 | Py_Finalize(); | ||
| 15 | |||
| 16 | return 0; | ||
| 17 | } | ||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* Simple program that repeatedly calls Py_Initialize(), does something, and | ||
| 2 | then calls Py_Finalize(). This should help finding leaks related to | ||
| 3 | initialization. */ | ||
| 4 | |||
| 5 | #include "Python.h" | ||
| 6 | |||
| 7 | main(int argc, char **argv) | ||
| 8 | { | ||
| 9 | int count = -1; | ||
| 10 | char *command; | ||
| 11 | |||
| 12 | if (argc < 2 || argc > 3) { | ||
| 13 | fprintf(stderr, "usage: loop <python-command> [count]\n"); | ||
| 14 | exit(2); | ||
| 15 | } | ||
| 16 | command = argv[1]; | ||
| 17 | |||
| 18 | if (argc == 3) { | ||
| 19 | count = atoi(argv[2]); | ||
| 20 | } | ||
| 21 | |||
| 22 | Py_SetProgramName(argv[0]); | ||
| 23 | |||
| 24 | /* uncomment this if you don't want to load site.py */ | ||
| 25 | /* Py_NoSiteFlag = 1; */ | ||
| 26 | |||
| 27 | while (count == -1 || --count >= 0 ) { | ||
| 28 | Py_Initialize(); | ||
| 29 | PyRun_SimpleString(command); | ||
| 30 | Py_Finalize(); | ||
| 31 | } | ||
| 32 | return 0; | ||
| 33 | } | ||
| @@ -0,0 +1,248 @@ | |||
| 1 | # | ||
| 2 | # importers.py | ||
| 3 | # | ||
| 4 | # Demonstration subclasses of imputil.Importer | ||
| 5 | # | ||
| 6 | |||
| 7 | # There should be consideration for the imports below if it is desirable | ||
| 8 | # to have "all" modules be imported through the imputil system. | ||
| 9 | |||
| 10 | # these are C extensions | ||
| 11 | import sys | ||
| 12 | import imp | ||
| 13 | import struct | ||
| 14 | import marshal | ||
| 15 | |||
| 16 | # these are .py modules | ||
| 17 | import imputil | ||
| 18 | import os | ||
| 19 | |||
| 20 | ###################################################################### | ||
| 21 | |||
| 22 | _TupleType = type(()) | ||
| 23 | _StringType = type('') | ||
| 24 | |||
| 25 | ###################################################################### | ||
| 26 | |||
| 27 | # byte-compiled file suffic character | ||
| 28 | _suffix_char = __debug__ and 'c' or 'o' | ||
| 29 | |||
| 30 | # byte-compiled file suffix | ||
| 31 | _suffix = '.py' + _suffix_char | ||
| 32 | |||
| 33 | # the C_EXTENSION suffixes | ||
| 34 | _c_suffixes = filter(lambda x: x[2] == imp.C_EXTENSION, imp.get_suffixes()) | ||
| 35 | |||
| 36 | def _timestamp(pathname): | ||
| 37 | "Return the file modification time as a Long." | ||
| 38 | try: | ||
| 39 | s = os.stat(pathname) | ||
| 40 | except OSError: | ||
| 41 | return None | ||
| 42 | return long(s[8]) | ||
| 43 | |||
| 44 | def _fs_import(dir, modname, fqname): | ||
| 45 | "Fetch a module from the filesystem." | ||
| 46 | |||
| 47 | pathname = os.path.join(dir, modname) | ||
| 48 | if os.path.isdir(pathname): | ||
| 49 | values = { '__pkgdir__' : pathname, '__path__' : [ pathname ] } | ||
| 50 | ispkg = 1 | ||
| 51 | pathname = os.path.join(pathname, '__init__') | ||
| 52 | else: | ||
| 53 | values = { } | ||
| 54 | ispkg = 0 | ||
| 55 | |||
| 56 | # look for dynload modules | ||
| 57 | for desc in _c_suffixes: | ||
| 58 | file = pathname + desc[0] | ||
| 59 | try: | ||
| 60 | fp = open(file, desc[1]) | ||
| 61 | except IOError: | ||
| 62 | pass | ||
| 63 | else: | ||
| 64 | module = imp.load_module(fqname, fp, file, desc) | ||
| 65 | values['__file__'] = file | ||
| 66 | return 0, module, values | ||
| 67 | |||
| 68 | t_py = _timestamp(pathname + '.py') | ||
| 69 | t_pyc = _timestamp(pathname + _suffix) | ||
| 70 | if t_py is None and t_pyc is None: | ||
| 71 | return None | ||
| 72 | code = None | ||
| 73 | if t_py is None or (t_pyc is not None and t_pyc >= t_py): | ||
| 74 | file = pathname + _suffix | ||
| 75 | f = open(file, 'rb') | ||
| 76 | if f.read(4) == imp.get_magic(): | ||
| 77 | t = struct.unpack('<I', f.read(4))[0] | ||
| 78 | if t == t_py: | ||
| 79 | code = marshal.load(f) | ||
| 80 | f.close() | ||
| 81 | if code is None: | ||
| 82 | file = pathname + '.py' | ||
| 83 | code = _compile(file, t_py) | ||
| 84 | |||
| 85 | values['__file__'] = file | ||
| 86 | return ispkg, code, values | ||
| 87 | |||
| 88 | ###################################################################### | ||
| 89 | # | ||
| 90 | # Simple function-based importer | ||
| 91 | # | ||
| 92 | class FuncImporter(imputil.Importer): | ||
| 93 | "Importer subclass to delegate to a function rather than method overrides." | ||
| 94 | def __init__(self, func): | ||
| 95 | self.func = func | ||
| 96 | def get_code(self, parent, modname, fqname): | ||
| 97 | return self.func(parent, modname, fqname) | ||
| 98 | |||
| 99 | def install_with(func): | ||
| 100 | FuncImporter(func).install() | ||
| 101 | |||
| 102 | |||
| 103 | ###################################################################### | ||
| 104 | # | ||
| 105 | # Base class for archive-based importing | ||
| 106 | # | ||
| 107 | class PackageArchiveImporter(imputil.Importer): | ||
| 108 | """Importer subclass to import from (file) archives. | ||
| 109 | |||
| 110 | This Importer handles imports of the style <archive>.<subfile>, where | ||
| 111 | <archive> can be located using a subclass-specific mechanism and the | ||
| 112 | <subfile> is found in the archive using a subclass-specific mechanism. | ||
| 113 | |||
| 114 | This class defines two hooks for subclasses: one to locate an archive | ||
| 115 | (and possibly return some context for future subfile lookups), and one | ||
| 116 | to locate subfiles. | ||
| 117 | """ | ||
| 118 | |||
| 119 | def get_code(self, parent, modname, fqname): | ||
| 120 | if parent: | ||
| 121 | # the Importer._finish_import logic ensures that we handle imports | ||
| 122 | # under the top level module (package / archive). | ||
| 123 | assert parent.__importer__ == self | ||
| 124 | |||
| 125 | # if a parent "package" is provided, then we are importing a | ||
| 126 | # sub-file from the archive. | ||
| 127 | result = self.get_subfile(parent.__archive__, modname) | ||
| 128 | if result is None: | ||
| 129 | return None | ||
| 130 | if isinstance(result, _TupleType): | ||
| 131 | assert len(result) == 2 | ||
| 132 | return (0,) + result | ||
| 133 | return 0, result, {} | ||
| 134 | |||
| 135 | # no parent was provided, so the archive should exist somewhere on the | ||
| 136 | # default "path". | ||
| 137 | archive = self.get_archive(modname) | ||
| 138 | if archive is None: | ||
| 139 | return None | ||
| 140 | return 1, "", {'__archive__':archive} | ||
| 141 | |||
| 142 | def get_archive(self, modname): | ||
| 143 | """Get an archive of modules. | ||
| 144 | |||
| 145 | This method should locate an archive and return a value which can be | ||
| 146 | used by get_subfile to load modules from it. The value may be a simple | ||
| 147 | pathname, an open file, or a complex object that caches information | ||
| 148 | for future imports. | ||
| 149 | |||
| 150 | Return None if the archive was not found. | ||
| 151 | """ | ||
| 152 | raise RuntimeError, "get_archive not implemented" | ||
| 153 | |||
| 154 | def get_subfile(self, archive, modname): | ||
| 155 | """Get code from a subfile in the specified archive. | ||
| 156 | |||
| 157 | Given the specified archive (as returned by get_archive()), locate | ||
| 158 | and return a code object for the specified module name. | ||
| 159 | |||
| 160 | A 2-tuple may be returned, consisting of a code object and a dict | ||
| 161 | of name/values to place into the target module. | ||
| 162 | |||
| 163 | Return None if the subfile was not found. | ||
| 164 | """ | ||
| 165 | raise RuntimeError, "get_subfile not implemented" | ||
| 166 | |||
| 167 | |||
| 168 | class PackageArchive(PackageArchiveImporter): | ||
| 169 | "PackageArchiveImporter subclass that refers to a specific archive." | ||
| 170 | |||
| 171 | def __init__(self, modname, archive_pathname): | ||
| 172 | self.__modname = modname | ||
| 173 | self.__path = archive_pathname | ||
| 174 | |||
| 175 | def get_archive(self, modname): | ||
| 176 | if modname == self.__modname: | ||
| 177 | return self.__path | ||
| 178 | return None | ||
| 179 | |||
| 180 | # get_subfile is passed the full pathname of the archive | ||
| 181 | |||
| 182 | |||
| 183 | ###################################################################### | ||
| 184 | # | ||
| 185 | # Emulate the standard directory-based import mechanism | ||
| 186 | # | ||
| 187 | class DirectoryImporter(imputil.Importer): | ||
| 188 | "Importer subclass to emulate the standard importer." | ||
| 189 | |||
| 190 | def __init__(self, dir): | ||
| 191 | self.dir = dir | ||
| 192 | |||
| 193 | def get_code(self, parent, modname, fqname): | ||
| 194 | if parent: | ||
| 195 | dir = parent.__pkgdir__ | ||
| 196 | else: | ||
| 197 | dir = self.dir | ||
| 198 | |||
| 199 | # Return the module (and other info) if found in the specified | ||
| 200 | # directory. Otherwise, return None. | ||
| 201 | return _fs_import(dir, modname, fqname) | ||
| 202 | |||
| 203 | def __repr__(self): | ||
| 204 | return '<%s.%s for "%s" at 0x%x>' % (self.__class__.__module__, | ||
| 205 | self.__class__.__name__, | ||
| 206 | self.dir, | ||
| 207 | id(self)) | ||
| 208 | |||
| 209 | |||
| 210 | ###################################################################### | ||
| 211 | # | ||
| 212 | # Emulate the standard path-style import mechanism | ||
| 213 | # | ||
| 214 | class PathImporter(imputil.Importer): | ||
| 215 | def __init__(self, path=sys.path): | ||
| 216 | self.path = path | ||
| 217 | |||
| 218 | def get_code(self, parent, modname, fqname): | ||
| 219 | if parent: | ||
| 220 | # we are looking for a module inside of a specific package | ||
| 221 | return _fs_import(parent.__pkgdir__, modname, fqname) | ||
| 222 | |||
| 223 | # scan sys.path, looking for the requested module | ||
| 224 | for dir in self.path: | ||
| 225 | if isinstance(dir, _StringType): | ||
| 226 | result = _fs_import(dir, modname, fqname) | ||
| 227 | if result: | ||
| 228 | return result | ||
| 229 | |||
| 230 | # not found | ||
| 231 | return None | ||
| 232 | |||
| 233 | ###################################################################### | ||
| 234 | |||
| 235 | def _test_dir(): | ||
| 236 | "Debug/test function to create DirectoryImporters from sys.path." | ||
| 237 | imputil.ImportManager().install() | ||
| 238 | path = sys.path[:] | ||
| 239 | path.reverse() | ||
| 240 | for d in path: | ||
| 241 | sys.path.insert(0, DirectoryImporter(d)) | ||
| 242 | sys.path.insert(0, imputil.BuiltinImporter()) | ||
| 243 | |||
| 244 | def _test_revamp(): | ||
| 245 | "Debug/test function for the revamped import system." | ||
| 246 | imputil.ImportManager().install() | ||
| 247 | sys.path.insert(0, PathImporter()) | ||
| 248 | sys.path.insert(0, imputil.BuiltinImporter()) | ||
| @@ -0,0 +1,126 @@ | |||
| 1 | """An Python re-implementation of hierarchical module import. | ||
| 2 | |||
| 3 | This code is intended to be read, not executed. However, it does work | ||
| 4 | -- all you need to do to enable it is "import knee". | ||
| 5 | |||
| 6 | (The name is a pun on the klunkier predecessor of this module, "ni".) | ||
| 7 | |||
| 8 | """ | ||
| 9 | |||
| 10 | import sys, imp, __builtin__ | ||
| 11 | |||
| 12 | |||
| 13 | # Replacement for __import__() | ||
| 14 | def import_hook(name, globals=None, locals=None, fromlist=None): | ||
| 15 | parent = determine_parent(globals) | ||
| 16 | q, tail = find_head_package(parent, name) | ||
| 17 | m = load_tail(q, tail) | ||
| 18 | if not fromlist: | ||
| 19 | return q | ||
| 20 | if hasattr(m, "__path__"): | ||
| 21 | ensure_fromlist(m, fromlist) | ||
| 22 | return m | ||
| 23 | |||
| 24 | def determine_parent(globals): | ||
| 25 | if not globals or not globals.has_key("__name__"): | ||
| 26 | return None | ||
| 27 | pname = globals['__name__'] | ||
| 28 | if globals.has_key("__path__"): | ||
| 29 | parent = sys.modules[pname] | ||
| 30 | assert globals is parent.__dict__ | ||
| 31 | return parent | ||
| 32 | if '.' in pname: | ||
| 33 | i = pname.rfind('.') | ||
| 34 | pname = pname[:i] | ||
| 35 | parent = sys.modules[pname] | ||
| 36 | assert parent.__name__ == pname | ||
| 37 | return parent | ||
| 38 | return None | ||
| 39 | |||
| 40 | def find_head_package(parent, name): | ||
| 41 | if '.' in name: | ||
| 42 | i = name.find('.') | ||
| 43 | head = name[:i] | ||
| 44 | tail = name[i+1:] | ||
| 45 | else: | ||
| 46 | head = name | ||
| 47 | tail = "" | ||
| 48 | if parent: | ||
| 49 | qname = "%s.%s" % (parent.__name__, head) | ||
| 50 | else: | ||
| 51 | qname = head | ||
| 52 | q = import_module(head, qname, parent) | ||
| 53 | if q: return q, tail | ||
| 54 | if parent: | ||
| 55 | qname = head | ||
| 56 | parent = None | ||
| 57 | q = import_module(head, qname, parent) | ||
| 58 | if q: return q, tail | ||
| 59 | raise ImportError, "No module named " + qname | ||
| 60 | |||
| 61 | def load_tail(q, tail): | ||
| 62 | m = q | ||
| 63 | while tail: | ||
| 64 | i = tail.find('.') | ||
| 65 | if i < 0: i = len(tail) | ||
| 66 | head, tail = tail[:i], tail[i+1:] | ||
| 67 | mname = "%s.%s" % (m.__name__, head) | ||
| 68 | m = import_module(head, mname, m) | ||
| 69 | if not m: | ||
| 70 | raise ImportError, "No module named " + mname | ||
| 71 | return m | ||
| 72 | |||
| 73 | def ensure_fromlist(m, fromlist, recursive=0): | ||
| 74 | for sub in fromlist: | ||
| 75 | if sub == "*": | ||
| 76 | if not recursive: | ||
| 77 | try: | ||
| 78 | all = m.__all__ | ||
| 79 | except AttributeError: | ||
| 80 | pass | ||
| 81 | else: | ||
| 82 | ensure_fromlist(m, all, 1) | ||
| 83 | continue | ||
| 84 | if sub != "*" and not hasattr(m, sub): | ||
| 85 | subname = "%s.%s" % (m.__name__, sub) | ||
| 86 | submod = import_module(sub, subname, m) | ||
| 87 | if not submod: | ||
| 88 | raise ImportError, "No module named " + subname | ||
| 89 | |||
| 90 | def import_module(partname, fqname, parent): | ||
| 91 | try: | ||
| 92 | return sys.modules[fqname] | ||
| 93 | except KeyError: | ||
| 94 | pass | ||
| 95 | try: | ||
| 96 | fp, pathname, stuff = imp.find_module(partname, | ||
| 97 | parent and parent.__path__) | ||
| 98 | except ImportError: | ||
| 99 | return None | ||
| 100 | try: | ||
| 101 | m = imp.load_module(fqname, fp, pathname, stuff) | ||
| 102 | finally: | ||
| 103 | if fp: fp.close() | ||
| 104 | if parent: | ||
| 105 | setattr(parent, partname, m) | ||
| 106 | return m | ||
| 107 | |||
| 108 | |||
| 109 | # Replacement for reload() | ||
| 110 | def reload_hook(module): | ||
| 111 | name = module.__name__ | ||
| 112 | if '.' not in name: | ||
| 113 | return import_module(name, name, None) | ||
| 114 | i = name.rfind('.') | ||
| 115 | pname = name[:i] | ||
| 116 | parent = sys.modules[pname] | ||
| 117 | return import_module(name[i+1:], name, parent) | ||
| 118 | |||
| 119 | |||
| 120 | # Save the original hooks | ||
| 121 | original_import = __builtin__.__import__ | ||
| 122 | original_reload = __builtin__.reload | ||
| 123 | |||
| 124 | # Now install our hooks | ||
| 125 | __builtin__.__import__ = import_hook | ||
| 126 | __builtin__.reload = reload_hook | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | This is the Python version of the MD5 test program from the MD5 | ||
| 2 | Internet Draft (Rivest and Dusse, The MD5 Message-Digest Algorithm, 10 | ||
| 3 | July 1991). The file "foo" contains the string "abc" with no trailing | ||
| 4 | newline. | ||
| 5 | |||
| 6 | When called without arguments, it acts as a filter. When called with | ||
| 7 | "-x", it executes a self-test, and the output should literally match | ||
| 8 | the output given in the RFC. | ||
| 9 | |||
| 10 | Code by Jan-Hein B\"uhrman after the original in C. | ||
| @@ -0,0 +1 @@ | |||
| 1 | abc | ||
| @@ -0,0 +1,123 @@ | |||
| 1 | import string | ||
| 2 | import md5 | ||
| 3 | from sys import argv | ||
| 4 | |||
| 5 | def MDPrint(str): | ||
| 6 | outstr = '' | ||
| 7 | for i in str: | ||
| 8 | o = ord(i) | ||
| 9 | outstr = (outstr | ||
| 10 | + string.hexdigits[(o >> 4) & 0xF] | ||
| 11 | + string.hexdigits[o & 0xF]) | ||
| 12 | print outstr, | ||
| 13 | |||
| 14 | |||
| 15 | from time import time | ||
| 16 | |||
| 17 | def makestr(start, end): | ||
| 18 | result = '' | ||
| 19 | for i in range(start, end + 1): | ||
| 20 | result = result + chr(i) | ||
| 21 | |||
| 22 | return result | ||
| 23 | |||
| 24 | |||
| 25 | def MDTimeTrial(): | ||
| 26 | TEST_BLOCK_SIZE = 1000 | ||
| 27 | TEST_BLOCKS = 10000 | ||
| 28 | |||
| 29 | TEST_BYTES = TEST_BLOCK_SIZE * TEST_BLOCKS | ||
| 30 | |||
| 31 | # initialize test data, need temporary string filler | ||
| 32 | |||
| 33 | filsiz = 1 << 8 | ||
| 34 | filler = makestr(0, filsiz-1) | ||
| 35 | data = filler * (TEST_BLOCK_SIZE / filsiz); | ||
| 36 | data = data + filler[:(TEST_BLOCK_SIZE % filsiz)] | ||
| 37 | |||
| 38 | del filsiz, filler | ||
| 39 | |||
| 40 | |||
| 41 | # start timer | ||
| 42 | print 'MD5 time trial. Processing', TEST_BYTES, 'characters...' | ||
| 43 | t1 = time() | ||
| 44 | |||
| 45 | mdContext = md5.new() | ||
| 46 | |||
| 47 | for i in range(TEST_BLOCKS): | ||
| 48 | mdContext.update(data) | ||
| 49 | |||
| 50 | str = mdContext.digest() | ||
| 51 | t2 = time() | ||
| 52 | |||
| 53 | MDPrint(str) | ||
| 54 | print 'is digest of test input.' | ||
| 55 | print 'Seconds to process test input:', t2 - t1 | ||
| 56 | print 'Characters processed per second:', TEST_BYTES / (t2 - t1) | ||
| 57 | |||
| 58 | |||
| 59 | def MDString(str): | ||
| 60 | MDPrint(md5.new(str).digest()) | ||
| 61 | print '"' + str + '"' | ||
| 62 | |||
| 63 | |||
| 64 | def MDFile(filename): | ||
| 65 | f = open(filename, 'rb'); | ||
| 66 | mdContext = md5.new() | ||
| 67 | |||
| 68 | while 1: | ||
| 69 | data = f.read(1024) | ||
| 70 | if not data: | ||
| 71 | break | ||
| 72 | mdContext.update(data) | ||
| 73 | |||
| 74 | MDPrint(mdContext.digest()) | ||
| 75 | print filename | ||
| 76 | |||
| 77 | |||
| 78 | import sys | ||
| 79 | |||
| 80 | def MDFilter(): | ||
| 81 | mdContext = md5.new() | ||
| 82 | |||
| 83 | while 1: | ||
| 84 | data = sys.stdin.read(16) | ||
| 85 | if not data: | ||
| 86 | break | ||
| 87 | mdContext.update(data) | ||
| 88 | |||
| 89 | MDPrint(mdContext.digest()) | ||
| 90 | |||
| 91 | |||
| 92 | |||
| 93 | def MDTestSuite(): | ||
| 94 | print 'MD5 test suite results:' | ||
| 95 | MDString('') | ||
| 96 | MDString('a') | ||
| 97 | MDString('abc') | ||
| 98 | MDString('message digest') | ||
| 99 | MDString(makestr(ord('a'), ord('z'))) | ||
| 100 | MDString(makestr(ord('A'), ord('Z')) | ||
| 101 | + makestr(ord('a'), ord('z')) | ||
| 102 | + makestr(ord('0'), ord('9'))) | ||
| 103 | MDString((makestr(ord('1'), ord('9')) + '0') * 8) | ||
| 104 | |||
| 105 | # Contents of file foo are "abc" | ||
| 106 | MDFile('foo') | ||
| 107 | |||
| 108 | |||
| 109 | # I don't wanna use getopt(), since I want to use the same i/f... | ||
| 110 | def main(): | ||
| 111 | if len(argv) == 1: | ||
| 112 | MDFilter() | ||
| 113 | for arg in argv[1:]: | ||
| 114 | if arg[:2] == '-s': | ||
| 115 | MDString(arg[2:]) | ||
| 116 | elif arg == '-t': | ||
| 117 | MDTimeTrial() | ||
| 118 | elif arg == '-x': | ||
| 119 | MDTestSuite() | ||
| 120 | else: | ||
| 121 | MDFile(arg) | ||
| 122 | |||
| 123 | main() | ||
| @@ -0,0 +1,113 @@ | |||
| 1 | """Support Eiffel-style preconditions and postconditions. | ||
| 2 | |||
| 3 | For example, | ||
| 4 | |||
| 5 | class C: | ||
| 6 | def m1(self, arg): | ||
| 7 | require arg > 0 | ||
| 8 | return whatever | ||
| 9 | ensure Result > arg | ||
| 10 | |||
| 11 | can be written (clumsily, I agree) as: | ||
| 12 | |||
| 13 | class C(Eiffel): | ||
| 14 | def m1(self, arg): | ||
| 15 | return whatever | ||
| 16 | def m1_pre(self, arg): | ||
| 17 | assert arg > 0 | ||
| 18 | def m1_post(self, Result, arg): | ||
| 19 | assert Result > arg | ||
| 20 | |||
| 21 | Pre- and post-conditions for a method, being implemented as methods | ||
| 22 | themselves, are inherited independently from the method. This gives | ||
| 23 | much of the same effect of Eiffel, where pre- and post-conditions are | ||
| 24 | inherited when a method is overridden by a derived class. However, | ||
| 25 | when a derived class in Python needs to extend a pre- or | ||
| 26 | post-condition, it must manually merge the base class' pre- or | ||
| 27 | post-condition with that defined in the derived class', for example: | ||
| 28 | |||
| 29 | class D(C): | ||
| 30 | def m1(self, arg): | ||
| 31 | return arg**2 | ||
| 32 | def m1_post(self, Result, arg): | ||
| 33 | C.m1_post(self, Result, arg) | ||
| 34 | assert Result < 100 | ||
| 35 | |||
| 36 | This gives derived classes more freedom but also more responsibility | ||
| 37 | than in Eiffel, where the compiler automatically takes care of this. | ||
| 38 | |||
| 39 | In Eiffel, pre-conditions combine using contravariance, meaning a | ||
| 40 | derived class can only make a pre-condition weaker; in Python, this is | ||
| 41 | up to the derived class. For example, a derived class that takes away | ||
| 42 | the requirement that arg > 0 could write: | ||
| 43 | |||
| 44 | def m1_pre(self, arg): | ||
| 45 | pass | ||
| 46 | |||
| 47 | but one could equally write a derived class that makes a stronger | ||
| 48 | requirement: | ||
| 49 | |||
| 50 | def m1_pre(self, arg): | ||
| 51 | require arg > 50 | ||
| 52 | |||
| 53 | It would be easy to modify the classes shown here so that pre- and | ||
| 54 | post-conditions can be disabled (separately, on a per-class basis). | ||
| 55 | |||
| 56 | A different design would have the pre- or post-condition testing | ||
| 57 | functions return true for success and false for failure. This would | ||
| 58 | make it possible to implement automatic combination of inherited | ||
| 59 | and new pre-/post-conditions. All this is left as an exercise to the | ||
| 60 | reader. | ||
| 61 | |||
| 62 | """ | ||
| 63 | |||
| 64 | from Meta import MetaClass, MetaHelper, MetaMethodWrapper | ||
| 65 | |||
| 66 | class EiffelMethodWrapper(MetaMethodWrapper): | ||
| 67 | |||
| 68 | def __init__(self, func, inst): | ||
| 69 | MetaMethodWrapper.__init__(self, func, inst) | ||
| 70 | # Note that the following causes recursive wrappers around | ||
| 71 | # the pre-/post-condition testing methods. These are harmless | ||
| 72 | # but inefficient; to avoid them, the lookup must be done | ||
| 73 | # using the class. | ||
| 74 | try: | ||
| 75 | self.pre = getattr(inst, self.__name__ + "_pre") | ||
| 76 | except AttributeError: | ||
| 77 | self.pre = None | ||
| 78 | try: | ||
| 79 | self.post = getattr(inst, self.__name__ + "_post") | ||
| 80 | except AttributeError: | ||
| 81 | self.post = None | ||
| 82 | |||
| 83 | def __call__(self, *args, **kw): | ||
| 84 | if self.pre: | ||
| 85 | apply(self.pre, args, kw) | ||
| 86 | Result = apply(self.func, (self.inst,) + args, kw) | ||
| 87 | if self.post: | ||
| 88 | apply(self.post, (Result,) + args, kw) | ||
| 89 | return Result | ||
| 90 | |||
| 91 | class EiffelHelper(MetaHelper): | ||
| 92 | __methodwrapper__ = EiffelMethodWrapper | ||
| 93 | |||
| 94 | class EiffelMetaClass(MetaClass): | ||
| 95 | __helper__ = EiffelHelper | ||
| 96 | |||
| 97 | Eiffel = EiffelMetaClass('Eiffel', (), {}) | ||
| 98 | |||
| 99 | |||
| 100 | def _test(): | ||
| 101 | class C(Eiffel): | ||
| 102 | def m1(self, arg): | ||
| 103 | return arg+1 | ||
| 104 | def m1_pre(self, arg): | ||
| 105 | assert arg > 0, "precondition for m1 failed" | ||
| 106 | def m1_post(self, Result, arg): | ||
| 107 | assert Result > arg | ||
| 108 | x = C() | ||
| 109 | x.m1(12) | ||
| 110 | ## x.m1(-1) | ||
| 111 | |||
| 112 | if __name__ == '__main__': | ||
| 113 | _test() | ||
| @@ -0,0 +1,169 @@ | |||
| 1 | """Enumeration metaclass. | ||
| 2 | |||
| 3 | XXX This is very much a work in progress. | ||
| 4 | |||
| 5 | """ | ||
| 6 | |||
| 7 | import string | ||
| 8 | |||
| 9 | class EnumMetaClass: | ||
| 10 | """Metaclass for enumeration. | ||
| 11 | |||
| 12 | To define your own enumeration, do something like | ||
| 13 | |||
| 14 | class Color(Enum): | ||
| 15 | red = 1 | ||
| 16 | green = 2 | ||
| 17 | blue = 3 | ||
| 18 | |||
| 19 | Now, Color.red, Color.green and Color.blue behave totally | ||
| 20 | different: they are enumerated values, not integers. | ||
| 21 | |||
| 22 | Enumerations cannot be instantiated; however they can be | ||
| 23 | subclassed. | ||
| 24 | |||
| 25 | """ | ||
| 26 | |||
| 27 | def __init__(self, name, bases, dict): | ||
| 28 | """Constructor -- create an enumeration. | ||
| 29 | |||
| 30 | Called at the end of the class statement. The arguments are | ||
| 31 | the name of the new class, a tuple containing the base | ||
| 32 | classes, and a dictionary containing everything that was | ||
| 33 | entered in the class' namespace during execution of the class | ||
| 34 | statement. In the above example, it would be {'red': 1, | ||
| 35 | 'green': 2, 'blue': 3}. | ||
| 36 | |||
| 37 | """ | ||
| 38 | for base in bases: | ||
| 39 | if base.__class__ is not EnumMetaClass: | ||
| 40 | raise TypeError, "Enumeration base class must be enumeration" | ||
| 41 | bases = filter(lambda x: x is not Enum, bases) | ||
| 42 | self.__name__ = name | ||
| 43 | self.__bases__ = bases | ||
| 44 | self.__dict = {} | ||
| 45 | for key, value in dict.items(): | ||
| 46 | self.__dict[key] = EnumInstance(name, key, value) | ||
| 47 | |||
| 48 | def __getattr__(self, name): | ||
| 49 | """Return an enumeration value. | ||
| 50 | |||
| 51 | For example, Color.red returns the value corresponding to red. | ||
| 52 | |||
| 53 | XXX Perhaps the values should be created in the constructor? | ||
| 54 | |||
| 55 | This looks in the class dictionary and if it is not found | ||
| 56 | there asks the base classes. | ||
| 57 | |||
| 58 | The special attribute __members__ returns the list of names | ||
| 59 | defined in this class (it does not merge in the names defined | ||
| 60 | in base classes). | ||
| 61 | |||
| 62 | """ | ||
| 63 | if name == '__members__': | ||
| 64 | return self.__dict.keys() | ||
| 65 | |||
| 66 | try: | ||
| 67 | return self.__dict[name] | ||
| 68 | except KeyError: | ||
| 69 | for base in self.__bases__: | ||
| 70 | try: | ||
| 71 | return getattr(base, name) | ||
| 72 | except AttributeError: | ||
| 73 | continue | ||
| 74 | |||
| 75 | raise AttributeError, name | ||
| 76 | |||
| 77 | def __repr__(self): | ||
| 78 | s = self.__name__ | ||
| 79 | if self.__bases__: | ||
| 80 | s = s + '(' + string.join(map(lambda x: x.__name__, | ||
| 81 | self.__bases__), ", ") + ')' | ||
| 82 | if self.__dict: | ||
| 83 | list = [] | ||
| 84 | for key, value in self.__dict.items(): | ||
| 85 | list.append("%s: %s" % (key, int(value))) | ||
| 86 | s = "%s: {%s}" % (s, string.join(list, ", ")) | ||
| 87 | return s | ||
| 88 | |||
| 89 | |||
| 90 | class EnumInstance: | ||
| 91 | """Class to represent an enumeration value. | ||
| 92 | |||
| 93 | EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves | ||
| 94 | like the integer 12 when compared, but doesn't support arithmetic. | ||
| 95 | |||
| 96 | XXX Should it record the actual enumeration rather than just its | ||
| 97 | name? | ||
| 98 | |||
| 99 | """ | ||
| 100 | |||
| 101 | def __init__(self, classname, enumname, value): | ||
| 102 | self.__classname = classname | ||
| 103 | self.__enumname = enumname | ||
| 104 | self.__value = value | ||
| 105 | |||
| 106 | def __int__(self): | ||
| 107 | return self.__value | ||
| 108 | |||
| 109 | def __repr__(self): | ||
| 110 | return "EnumInstance(%r, %r, %r)" % (self.__classname, | ||
| 111 | self.__enumname, | ||
| 112 | self.__value) | ||
| 113 | |||
| 114 | def __str__(self): | ||
| 115 | return "%s.%s" % (self.__classname, self.__enumname) | ||
| 116 | |||
| 117 | def __cmp__(self, other): | ||
| 118 | return cmp(self.__value, int(other)) | ||
| 119 | |||
| 120 | |||
| 121 | # Create the base class for enumerations. | ||
| 122 | # It is an empty enumeration. | ||
| 123 | Enum = EnumMetaClass("Enum", (), {}) | ||
| 124 | |||
| 125 | |||
| 126 | def _test(): | ||
| 127 | |||
| 128 | class Color(Enum): | ||
| 129 | red = 1 | ||
| 130 | green = 2 | ||
| 131 | blue = 3 | ||
| 132 | |||
| 133 | print Color.red | ||
| 134 | print dir(Color) | ||
| 135 | |||
| 136 | print Color.red == Color.red | ||
| 137 | print Color.red == Color.blue | ||
| 138 | print Color.red == 1 | ||
| 139 | print Color.red == 2 | ||
| 140 | |||
| 141 | class ExtendedColor(Color): | ||
| 142 | white = 0 | ||
| 143 | orange = 4 | ||
| 144 | yellow = 5 | ||
| 145 | purple = 6 | ||
| 146 | black = 7 | ||
| 147 | |||
| 148 | print ExtendedColor.orange | ||
| 149 | print ExtendedColor.red | ||
| 150 | |||
| 151 | print Color.red == ExtendedColor.red | ||
| 152 | |||
| 153 | class OtherColor(Enum): | ||
| 154 | white = 4 | ||
| 155 | blue = 5 | ||
| 156 | |||
| 157 | class MergedColor(Color, OtherColor): | ||
| 158 | pass | ||
| 159 | |||
| 160 | print MergedColor.red | ||
| 161 | print MergedColor.white | ||
| 162 | |||
| 163 | print Color | ||
| 164 | print ExtendedColor | ||
| 165 | print OtherColor | ||
| 166 | print MergedColor | ||
| 167 | |||
| 168 | if __name__ == '__main__': | ||
| 169 | _test() | ||
| @@ -0,0 +1,118 @@ | |||
| 1 | """Generic metaclass. | ||
| 2 | |||
| 3 | XXX This is very much a work in progress. | ||
| 4 | |||
| 5 | """ | ||
| 6 | |||
| 7 | import types | ||
| 8 | |||
| 9 | class MetaMethodWrapper: | ||
| 10 | |||
| 11 | def __init__(self, func, inst): | ||
| 12 | self.func = func | ||
| 13 | self.inst = inst | ||
| 14 | self.__name__ = self.func.__name__ | ||
| 15 | |||
| 16 | def __call__(self, *args, **kw): | ||
| 17 | return apply(self.func, (self.inst,) + args, kw) | ||
| 18 | |||
| 19 | class MetaHelper: | ||
| 20 | |||
| 21 | __methodwrapper__ = MetaMethodWrapper # For derived helpers to override | ||
| 22 | |||
| 23 | def __helperinit__(self, formalclass): | ||
| 24 | self.__formalclass__ = formalclass | ||
| 25 | |||
| 26 | def __getattr__(self, name): | ||
| 27 | # Invoked for any attr not in the instance's __dict__ | ||
| 28 | try: | ||
| 29 | raw = self.__formalclass__.__getattr__(name) | ||
| 30 | except AttributeError: | ||
| 31 | try: | ||
| 32 | ga = self.__formalclass__.__getattr__('__usergetattr__') | ||
| 33 | except (KeyError, AttributeError): | ||
| 34 | raise AttributeError, name | ||
| 35 | return ga(self, name) | ||
| 36 | if type(raw) != types.FunctionType: | ||
| 37 | return raw | ||
| 38 | return self.__methodwrapper__(raw, self) | ||
| 39 | |||
| 40 | class MetaClass: | ||
| 41 | |||
| 42 | """A generic metaclass. | ||
| 43 | |||
| 44 | This can be subclassed to implement various kinds of meta-behavior. | ||
| 45 | |||
| 46 | """ | ||
| 47 | |||
| 48 | __helper__ = MetaHelper # For derived metaclasses to override | ||
| 49 | |||
| 50 | __inited = 0 | ||
| 51 | |||
| 52 | def __init__(self, name, bases, dict): | ||
| 53 | try: | ||
| 54 | ga = dict['__getattr__'] | ||
| 55 | except KeyError: | ||
| 56 | pass | ||
| 57 | else: | ||
| 58 | dict['__usergetattr__'] = ga | ||
| 59 | del dict['__getattr__'] | ||
| 60 | self.__name__ = name | ||
| 61 | self.__bases__ = bases | ||
| 62 | self.__realdict__ = dict | ||
| 63 | self.__inited = 1 | ||
| 64 | |||
| 65 | def __getattr__(self, name): | ||
| 66 | try: | ||
| 67 | return self.__realdict__[name] | ||
| 68 | except KeyError: | ||
| 69 | for base in self.__bases__: | ||
| 70 | try: | ||
| 71 | return base.__getattr__(name) | ||
| 72 | except AttributeError: | ||
| 73 | pass | ||
| 74 | raise AttributeError, name | ||
| 75 | |||
| 76 | def __setattr__(self, name, value): | ||
| 77 | if not self.__inited: | ||
| 78 | self.__dict__[name] = value | ||
| 79 | else: | ||
| 80 | self.__realdict__[name] = value | ||
| 81 | |||
| 82 | def __call__(self, *args, **kw): | ||
| 83 | inst = self.__helper__() | ||
| 84 | inst.__helperinit__(self) | ||
| 85 | try: | ||
| 86 | init = inst.__getattr__('__init__') | ||
| 87 | except AttributeError: | ||
| 88 | init = lambda: None | ||
| 89 | apply(init, args, kw) | ||
| 90 | return inst | ||
| 91 | |||
| 92 | |||
| 93 | Meta = MetaClass('Meta', (), {}) | ||
| 94 | |||
| 95 | |||
| 96 | def _test(): | ||
| 97 | class C(Meta): | ||
| 98 | def __init__(self, *args): | ||
| 99 | print "__init__, args =", args | ||
| 100 | def m1(self, x): | ||
| 101 | print "m1(x=%r)" % (x,) | ||
| 102 | print C | ||
| 103 | x = C() | ||
| 104 | print x | ||
| 105 | x.m1(12) | ||
| 106 | class D(C): | ||
| 107 | def __getattr__(self, name): | ||
| 108 | if name[:2] == '__': raise AttributeError, name | ||
| 109 | return "getattr:%s" % name | ||
| 110 | x = D() | ||
| 111 | print x.foo | ||
| 112 | print x._foo | ||
| 113 | ## print x.__foo | ||
| 114 | ## print x.__foo__ | ||
| 115 | |||
| 116 | |||
| 117 | if __name__ == '__main__': | ||
| 118 | _test() | ||
| @@ -0,0 +1,45 @@ | |||
| 1 | import types | ||
| 2 | |||
| 3 | class Tracing: | ||
| 4 | def __init__(self, name, bases, namespace): | ||
| 5 | """Create a new class.""" | ||
| 6 | self.__name__ = name | ||
| 7 | self.__bases__ = bases | ||
| 8 | self.__namespace__ = namespace | ||
| 9 | def __call__(self): | ||
| 10 | """Create a new instance.""" | ||
| 11 | return Instance(self) | ||
| 12 | |||
| 13 | class Instance: | ||
| 14 | def __init__(self, klass): | ||
| 15 | self.__klass__ = klass | ||
| 16 | def __getattr__(self, name): | ||
| 17 | try: | ||
| 18 | value = self.__klass__.__namespace__[name] | ||
| 19 | except KeyError: | ||
| 20 | raise AttributeError, name | ||
| 21 | if type(value) is not types.FunctionType: | ||
| 22 | return value | ||
| 23 | return BoundMethod(value, self) | ||
| 24 | |||
| 25 | class BoundMethod: | ||
| 26 | def __init__(self, function, instance): | ||
| 27 | self.function = function | ||
| 28 | self.instance = instance | ||
| 29 | def __call__(self, *args): | ||
| 30 | print "calling", self.function, "for", self.instance, "with", args | ||
| 31 | return apply(self.function, (self.instance,) + args) | ||
| 32 | |||
| 33 | Trace = Tracing('Trace', (), {}) | ||
| 34 | |||
| 35 | class MyTracedClass(Trace): | ||
| 36 | def method1(self, a): | ||
| 37 | self.a = a | ||
| 38 | def method2(self): | ||
| 39 | return self.a | ||
| 40 | |||
| 41 | aninstance = MyTracedClass() | ||
| 42 | |||
| 43 | aninstance.method1(10) | ||
| 44 | |||
| 45 | print aninstance.method2() | ||
| @@ -0,0 +1,256 @@ | |||
| 1 | """Synchronization metaclass. | ||
| 2 | |||
| 3 | This metaclass makes it possible to declare synchronized methods. | ||
| 4 | |||
| 5 | """ | ||
| 6 | |||
| 7 | import thread | ||
| 8 | |||
| 9 | # First we need to define a reentrant lock. | ||
| 10 | # This is generally useful and should probably be in a standard Python | ||
| 11 | # library module. For now, we in-line it. | ||
| 12 | |||
| 13 | class Lock: | ||
| 14 | |||
| 15 | """Reentrant lock. | ||
| 16 | |||
| 17 | This is a mutex-like object which can be acquired by the same | ||
| 18 | thread more than once. It keeps a reference count of the number | ||
| 19 | of times it has been acquired by the same thread. Each acquire() | ||
| 20 | call must be matched by a release() call and only the last | ||
| 21 | release() call actually releases the lock for acquisition by | ||
| 22 | another thread. | ||
| 23 | |||
| 24 | The implementation uses two locks internally: | ||
| 25 | |||
| 26 | __mutex is a short term lock used to protect the instance variables | ||
| 27 | __wait is the lock for which other threads wait | ||
| 28 | |||
| 29 | A thread intending to acquire both locks should acquire __wait | ||
| 30 | first. | ||
| 31 | |||
| 32 | The implementation uses two other instance variables, protected by | ||
| 33 | locking __mutex: | ||
| 34 | |||
| 35 | __tid is the thread ID of the thread that currently has the lock | ||
| 36 | __count is the number of times the current thread has acquired it | ||
| 37 | |||
| 38 | When the lock is released, __tid is None and __count is zero. | ||
| 39 | |||
| 40 | """ | ||
| 41 | |||
| 42 | def __init__(self): | ||
| 43 | """Constructor. Initialize all instance variables.""" | ||
| 44 | self.__mutex = thread.allocate_lock() | ||
| 45 | self.__wait = thread.allocate_lock() | ||
| 46 | self.__tid = None | ||
| 47 | self.__count = 0 | ||
| 48 | |||
| 49 | def acquire(self, flag=1): | ||
| 50 | """Acquire the lock. | ||
| 51 | |||
| 52 | If the optional flag argument is false, returns immediately | ||
| 53 | when it cannot acquire the __wait lock without blocking (it | ||
| 54 | may still block for a little while in order to acquire the | ||
| 55 | __mutex lock). | ||
| 56 | |||
| 57 | The return value is only relevant when the flag argument is | ||
| 58 | false; it is 1 if the lock is acquired, 0 if not. | ||
| 59 | |||
| 60 | """ | ||
| 61 | self.__mutex.acquire() | ||
| 62 | try: | ||
| 63 | if self.__tid == thread.get_ident(): | ||
| 64 | self.__count = self.__count + 1 | ||
| 65 | return 1 | ||
| 66 | finally: | ||
| 67 | self.__mutex.release() | ||
| 68 | locked = self.__wait.acquire(flag) | ||
| 69 | if not flag and not locked: | ||
| 70 | return 0 | ||
| 71 | try: | ||
| 72 | self.__mutex.acquire() | ||
| 73 | assert self.__tid == None | ||
| 74 | assert self.__count == 0 | ||
| 75 | self.__tid = thread.get_ident() | ||
| 76 | self.__count = 1 | ||
| 77 | return 1 | ||
| 78 | finally: | ||
| 79 | self.__mutex.release() | ||
| 80 | |||
| 81 | def release(self): | ||
| 82 | """Release the lock. | ||
| 83 | |||
| 84 | If this thread doesn't currently have the lock, an assertion | ||
| 85 | error is raised. | ||
| 86 | |||
| 87 | Only allow another thread to acquire the lock when the count | ||
| 88 | reaches zero after decrementing it. | ||
| 89 | |||
| 90 | """ | ||
| 91 | self.__mutex.acquire() | ||
| 92 | try: | ||
| 93 | assert self.__tid == thread.get_ident() | ||
| 94 | assert self.__count > 0 | ||
| 95 | self.__count = self.__count - 1 | ||
| 96 | if self.__count == 0: | ||
| 97 | self.__tid = None | ||
| 98 | self.__wait.release() | ||
| 99 | finally: | ||
| 100 | self.__mutex.release() | ||
| 101 | |||
| 102 | |||
| 103 | def _testLock(): | ||
| 104 | |||
| 105 | done = [] | ||
| 106 | |||
| 107 | def f2(lock, done=done): | ||
| 108 | lock.acquire() | ||
| 109 | print "f2 running in thread %d\n" % thread.get_ident(), | ||
| 110 | lock.release() | ||
| 111 | done.append(1) | ||
| 112 | |||
| 113 | def f1(lock, f2=f2, done=done): | ||
| 114 | lock.acquire() | ||
| 115 | print "f1 running in thread %d\n" % thread.get_ident(), | ||
| 116 | try: | ||
| 117 | f2(lock) | ||
| 118 | finally: | ||
| 119 | lock.release() | ||
| 120 | done.append(1) | ||
| 121 | |||
| 122 | lock = Lock() | ||
| 123 | lock.acquire() | ||
| 124 | f1(lock) # Adds 2 to done | ||
| 125 | lock.release() | ||
| 126 | |||
| 127 | lock.acquire() | ||
| 128 | |||
| 129 | thread.start_new_thread(f1, (lock,)) # Adds 2 | ||
| 130 | thread.start_new_thread(f1, (lock, f1)) # Adds 3 | ||
| 131 | thread.start_new_thread(f2, (lock,)) # Adds 1 | ||
| 132 | thread.start_new_thread(f2, (lock,)) # Adds 1 | ||
| 133 | |||
| 134 | lock.release() | ||
| 135 | import time | ||
| 136 | while len(done) < 9: | ||
| 137 | print len(done) | ||
| 138 | time.sleep(0.001) | ||
| 139 | print len(done) | ||
| 140 | |||
| 141 | |||
| 142 | # Now, the Locking metaclass is a piece of cake. | ||
| 143 | # As an example feature, methods whose name begins with exactly one | ||
| 144 | # underscore are not synchronized. | ||
| 145 | |||
| 146 | from Meta import MetaClass, MetaHelper, MetaMethodWrapper | ||
| 147 | |||
| 148 | class LockingMethodWrapper(MetaMethodWrapper): | ||
| 149 | def __call__(self, *args, **kw): | ||
| 150 | if self.__name__[:1] == '_' and self.__name__[1:] != '_': | ||
| 151 | return apply(self.func, (self.inst,) + args, kw) | ||
| 152 | self.inst.__lock__.acquire() | ||
| 153 | try: | ||
| 154 | return apply(self.func, (self.inst,) + args, kw) | ||
| 155 | finally: | ||
| 156 | self.inst.__lock__.release() | ||
| 157 | |||
| 158 | class LockingHelper(MetaHelper): | ||
| 159 | __methodwrapper__ = LockingMethodWrapper | ||
| 160 | def __helperinit__(self, formalclass): | ||
| 161 | MetaHelper.__helperinit__(self, formalclass) | ||
| 162 | self.__lock__ = Lock() | ||
| 163 | |||
| 164 | class LockingMetaClass(MetaClass): | ||
| 165 | __helper__ = LockingHelper | ||
| 166 | |||
| 167 | Locking = LockingMetaClass('Locking', (), {}) | ||
| 168 | |||
| 169 | def _test(): | ||
| 170 | # For kicks, take away the Locking base class and see it die | ||
| 171 | class Buffer(Locking): | ||
| 172 | def __init__(self, initialsize): | ||
| 173 | assert initialsize > 0 | ||
| 174 | self.size = initialsize | ||
| 175 | self.buffer = [None]*self.size | ||
| 176 | self.first = self.last = 0 | ||
| 177 | def put(self, item): | ||
| 178 | # Do we need to grow the buffer? | ||
| 179 | if (self.last+1) % self.size != self.first: | ||
| 180 | # Insert the new item | ||
| 181 | self.buffer[self.last] = item | ||
| 182 | self.last = (self.last+1) % self.size | ||
| 183 | return | ||
| 184 | # Double the buffer size | ||
| 185 | # First normalize it so that first==0 and last==size-1 | ||
| 186 | print "buffer =", self.buffer | ||
| 187 | print "first = %d, last = %d, size = %d" % ( | ||
| 188 | self.first, self.last, self.size) | ||
| 189 | if self.first <= self.last: | ||
| 190 | temp = self.buffer[self.first:self.last] | ||
| 191 | else: | ||
| 192 | temp = self.buffer[self.first:] + self.buffer[:self.last] | ||
| 193 | print "temp =", temp | ||
| 194 | self.buffer = temp + [None]*(self.size+1) | ||
| 195 | self.first = 0 | ||
| 196 | self.last = self.size-1 | ||
| 197 | self.size = self.size*2 | ||
| 198 | print "Buffer size doubled to", self.size | ||
| 199 | print "new buffer =", self.buffer | ||
| 200 | print "first = %d, last = %d, size = %d" % ( | ||
| 201 | self.first, self.last, self.size) | ||
| 202 | self.put(item) # Recursive call to test the locking | ||
| 203 | def get(self): | ||
| 204 | # Is the buffer empty? | ||
| 205 | if self.first == self.last: | ||
| 206 | raise EOFError # Avoid defining a new exception | ||
| 207 | item = self.buffer[self.first] | ||
| 208 | self.first = (self.first+1) % self.size | ||
| 209 | return item | ||
| 210 | |||
| 211 | def producer(buffer, wait, n=1000): | ||
| 212 | import time | ||
| 213 | i = 0 | ||
| 214 | while i < n: | ||
| 215 | print "put", i | ||
| 216 | buffer.put(i) | ||
| 217 | i = i+1 | ||
| 218 | print "Producer: done producing", n, "items" | ||
| 219 | wait.release() | ||
| 220 | |||
| 221 | def consumer(buffer, wait, n=1000): | ||
| 222 | import time | ||
| 223 | i = 0 | ||
| 224 | tout = 0.001 | ||
| 225 | while i < n: | ||
| 226 | try: | ||
| 227 | x = buffer.get() | ||
| 228 | if x != i: | ||
| 229 | raise AssertionError, \ | ||
| 230 | "get() returned %s, expected %s" % (x, i) | ||
| 231 | print "got", i | ||
| 232 | i = i+1 | ||
| 233 | tout = 0.001 | ||
| 234 | except EOFError: | ||
| 235 | time.sleep(tout) | ||
| 236 | tout = tout*2 | ||
| 237 | print "Consumer: done consuming", n, "items" | ||
| 238 | wait.release() | ||
| 239 | |||
| 240 | pwait = thread.allocate_lock() | ||
| 241 | pwait.acquire() | ||
| 242 | cwait = thread.allocate_lock() | ||
| 243 | cwait.acquire() | ||
| 244 | buffer = Buffer(1) | ||
| 245 | n = 1000 | ||
| 246 | thread.start_new_thread(consumer, (buffer, cwait, n)) | ||
| 247 | thread.start_new_thread(producer, (buffer, pwait, n)) | ||
| 248 | pwait.acquire() | ||
| 249 | print "Producer done" | ||
| 250 | cwait.acquire() | ||
| 251 | print "All done" | ||
| 252 | print "buffer size ==", len(buffer.buffer) | ||
| 253 | |||
| 254 | if __name__ == '__main__': | ||
| 255 | _testLock() | ||
| 256 | _test() | ||
| @@ -0,0 +1,144 @@ | |||
| 1 | """Tracing metaclass. | ||
| 2 | |||
| 3 | XXX This is very much a work in progress. | ||
| 4 | |||
| 5 | """ | ||
| 6 | |||
| 7 | import types, sys | ||
| 8 | |||
| 9 | class TraceMetaClass: | ||
| 10 | """Metaclass for tracing. | ||
| 11 | |||
| 12 | Classes defined using this metaclass have an automatic tracing | ||
| 13 | feature -- by setting the __trace_output__ instance (or class) | ||
| 14 | variable to a file object, trace messages about all calls are | ||
| 15 | written to the file. The trace formatting can be changed by | ||
| 16 | defining a suitable __trace_call__ method. | ||
| 17 | |||
| 18 | """ | ||
| 19 | |||
| 20 | __inited = 0 | ||
| 21 | |||
| 22 | def __init__(self, name, bases, dict): | ||
| 23 | self.__name__ = name | ||
| 24 | self.__bases__ = bases | ||
| 25 | self.__dict = dict | ||
| 26 | # XXX Can't define __dict__, alas | ||
| 27 | self.__inited = 1 | ||
| 28 | |||
| 29 | def __getattr__(self, name): | ||
| 30 | try: | ||
| 31 | return self.__dict[name] | ||
| 32 | except KeyError: | ||
| 33 | for base in self.__bases__: | ||
| 34 | try: | ||
| 35 | return base.__getattr__(name) | ||
| 36 | except AttributeError: | ||
| 37 | pass | ||
| 38 | raise AttributeError, name | ||
| 39 | |||
| 40 | def __setattr__(self, name, value): | ||
| 41 | if not self.__inited: | ||
| 42 | self.__dict__[name] = value | ||
| 43 | else: | ||
| 44 | self.__dict[name] = value | ||
| 45 | |||
| 46 | def __call__(self, *args, **kw): | ||
| 47 | inst = TracingInstance() | ||
| 48 | inst.__meta_init__(self) | ||
| 49 | try: | ||
| 50 | init = inst.__getattr__('__init__') | ||
| 51 | except AttributeError: | ||
| 52 | init = lambda: None | ||
| 53 | apply(init, args, kw) | ||
| 54 | return inst | ||
| 55 | |||
| 56 | __trace_output__ = None | ||
| 57 | |||
| 58 | class TracingInstance: | ||
| 59 | """Helper class to represent an instance of a tracing class.""" | ||
| 60 | |||
| 61 | def __trace_call__(self, fp, fmt, *args): | ||
| 62 | fp.write((fmt+'\n') % args) | ||
| 63 | |||
| 64 | def __meta_init__(self, klass): | ||
| 65 | self.__class = klass | ||
| 66 | |||
| 67 | def __getattr__(self, name): | ||
| 68 | # Invoked for any attr not in the instance's __dict__ | ||
| 69 | try: | ||
| 70 | raw = self.__class.__getattr__(name) | ||
| 71 | except AttributeError: | ||
| 72 | raise AttributeError, name | ||
| 73 | if type(raw) != types.FunctionType: | ||
| 74 | return raw | ||
| 75 | # It's a function | ||
| 76 | fullname = self.__class.__name__ + "." + name | ||
| 77 | if not self.__trace_output__ or name == '__trace_call__': | ||
| 78 | return NotTracingWrapper(fullname, raw, self) | ||
| 79 | else: | ||
| 80 | return TracingWrapper(fullname, raw, self) | ||
| 81 | |||
| 82 | class NotTracingWrapper: | ||
| 83 | def __init__(self, name, func, inst): | ||
| 84 | self.__name__ = name | ||
| 85 | self.func = func | ||
| 86 | self.inst = inst | ||
| 87 | def __call__(self, *args, **kw): | ||
| 88 | return apply(self.func, (self.inst,) + args, kw) | ||
| 89 | |||
| 90 | class TracingWrapper(NotTracingWrapper): | ||
| 91 | def __call__(self, *args, **kw): | ||
| 92 | self.inst.__trace_call__(self.inst.__trace_output__, | ||
| 93 | "calling %s, inst=%s, args=%s, kw=%s", | ||
| 94 | self.__name__, self.inst, args, kw) | ||
| 95 | try: | ||
| 96 | rv = apply(self.func, (self.inst,) + args, kw) | ||
| 97 | except: | ||
| 98 | t, v, tb = sys.exc_info() | ||
| 99 | self.inst.__trace_call__(self.inst.__trace_output__, | ||
| 100 | "returning from %s with exception %s: %s", | ||
| 101 | self.__name__, t, v) | ||
| 102 | raise t, v, tb | ||
| 103 | else: | ||
| 104 | self.inst.__trace_call__(self.inst.__trace_output__, | ||
| 105 | "returning from %s with value %s", | ||
| 106 | self.__name__, rv) | ||
| 107 | return rv | ||
| 108 | |||
| 109 | Traced = TraceMetaClass('Traced', (), {'__trace_output__': None}) | ||
| 110 | |||
| 111 | |||
| 112 | def _test(): | ||
| 113 | global C, D | ||
| 114 | class C(Traced): | ||
| 115 | def __init__(self, x=0): self.x = x | ||
| 116 | def m1(self, x): self.x = x | ||
| 117 | def m2(self, y): return self.x + y | ||
| 118 | __trace_output__ = sys.stdout | ||
| 119 | class D(C): | ||
| 120 | def m2(self, y): print "D.m2(%r)" % (y,); return C.m2(self, y) | ||
| 121 | __trace_output__ = None | ||
| 122 | x = C(4321) | ||
| 123 | print x | ||
| 124 | print x.x | ||
| 125 | print x.m1(100) | ||
| 126 | print x.m1(10) | ||
| 127 | print x.m2(33) | ||
| 128 | print x.m1(5) | ||
| 129 | print x.m2(4000) | ||
| 130 | print x.x | ||
| 131 | |||
| 132 | print C.__init__ | ||
| 133 | print C.m2 | ||
| 134 | print D.__init__ | ||
| 135 | print D.m2 | ||
| 136 | |||
| 137 | y = D() | ||
| 138 | print y | ||
| 139 | print y.m1(10) | ||
| 140 | print y.m2(100) | ||
| 141 | print y.x | ||
| 142 | |||
| 143 | if __name__ == '__main__': | ||
| 144 | _test() | ||
| @@ -0,0 +1,605 @@ | |||
| 1 | <HTML> | ||
| 2 | |||
| 3 | <HEAD> | ||
| 4 | <TITLE>Metaclasses in Python 1.5</TITLE> | ||
| 5 | </HEAD> | ||
| 6 | |||
| 7 | <BODY BGCOLOR="FFFFFF"> | ||
| 8 | |||
| 9 | <H1>Metaclasses in Python 1.5</H1> | ||
| 10 | <H2>(A.k.a. The Killer Joke :-)</H2> | ||
| 11 | |||
| 12 | <HR> | ||
| 13 | |||
| 14 | (<i>Postscript:</i> reading this essay is probably not the best way to | ||
| 15 | understand the metaclass hook described here. See a <A | ||
| 16 | HREF="meta-vladimir.txt">message posted by Vladimir Marangozov</A> | ||
| 17 | which may give a gentler introduction to the matter. You may also | ||
| 18 | want to search Deja News for messages with "metaclass" in the subject | ||
| 19 | posted to comp.lang.python in July and August 1998.) | ||
| 20 | |||
| 21 | <HR> | ||
| 22 | |||
| 23 | <P>In previous Python releases (and still in 1.5), there is something | ||
| 24 | called the ``Don Beaudry hook'', after its inventor and champion. | ||
| 25 | This allows C extensions to provide alternate class behavior, thereby | ||
| 26 | allowing the Python class syntax to be used to define other class-like | ||
| 27 | entities. Don Beaudry has used this in his infamous <A | ||
| 28 | HREF="http://maigret.cog.brown.edu/pyutil/">MESS</A> package; Jim | ||
| 29 | Fulton has used it in his <A | ||
| 30 | HREF="http://www.digicool.com/releases/ExtensionClass/">Extension | ||
| 31 | Classes</A> package. (It has also been referred to as the ``Don | ||
| 32 | Beaudry <i>hack</i>,'' but that's a misnomer. There's nothing hackish | ||
| 33 | about it -- in fact, it is rather elegant and deep, even though | ||
| 34 | there's something dark to it.) | ||
| 35 | |||
| 36 | <P>(On first reading, you may want to skip directly to the examples in | ||
| 37 | the section "Writing Metaclasses in Python" below, unless you want | ||
| 38 | your head to explode.) | ||
| 39 | |||
| 40 | <P> | ||
| 41 | |||
| 42 | <HR> | ||
| 43 | |||
| 44 | <P>Documentation of the Don Beaudry hook has purposefully been kept | ||
| 45 | minimal, since it is a feature of incredible power, and is easily | ||
| 46 | abused. Basically, it checks whether the <b>type of the base | ||
| 47 | class</b> is callable, and if so, it is called to create the new | ||
| 48 | class. | ||
| 49 | |||
| 50 | <P>Note the two indirection levels. Take a simple example: | ||
| 51 | |||
| 52 | <PRE> | ||
| 53 | class B: | ||
| 54 | pass | ||
| 55 | |||
| 56 | class C(B): | ||
| 57 | pass | ||
| 58 | </PRE> | ||
| 59 | |||
| 60 | Take a look at the second class definition, and try to fathom ``the | ||
| 61 | type of the base class is callable.'' | ||
| 62 | |||
| 63 | <P>(Types are not classes, by the way. See questions 4.2, 4.19 and in | ||
| 64 | particular 6.22 in the <A | ||
| 65 | HREF="http://www.python.org/cgi-bin/faqw.py" >Python FAQ</A> | ||
| 66 | for more on this topic.) | ||
| 67 | |||
| 68 | <P> | ||
| 69 | |||
| 70 | <UL> | ||
| 71 | |||
| 72 | <LI>The <b>base class</b> is B; this one's easy.<P> | ||
| 73 | |||
| 74 | <LI>Since B is a class, its type is ``class''; so the <b>type of the | ||
| 75 | base class</b> is the type ``class''. This is also known as | ||
| 76 | types.ClassType, assuming the standard module <code>types</code> has | ||
| 77 | been imported.<P> | ||
| 78 | |||
| 79 | <LI>Now is the type ``class'' <b>callable</b>? No, because types (in | ||
| 80 | core Python) are never callable. Classes are callable (calling a | ||
| 81 | class creates a new instance) but types aren't.<P> | ||
| 82 | |||
| 83 | </UL> | ||
| 84 | |||
| 85 | <P>So our conclusion is that in our example, the type of the base | ||
| 86 | class (of C) is not callable. So the Don Beaudry hook does not apply, | ||
| 87 | and the default class creation mechanism is used (which is also used | ||
| 88 | when there is no base class). In fact, the Don Beaudry hook never | ||
| 89 | applies when using only core Python, since the type of a core object | ||
| 90 | is never callable. | ||
| 91 | |||
| 92 | <P>So what do Don and Jim do in order to use Don's hook? Write an | ||
| 93 | extension that defines at least two new Python object types. The | ||
| 94 | first would be the type for ``class-like'' objects usable as a base | ||
| 95 | class, to trigger Don's hook. This type must be made callable. | ||
| 96 | That's why we need a second type. Whether an object is callable | ||
| 97 | depends on its type. So whether a type object is callable depends on | ||
| 98 | <i>its</i> type, which is a <i>meta-type</i>. (In core Python there | ||
| 99 | is only one meta-type, the type ``type'' (types.TypeType), which is | ||
| 100 | the type of all type objects, even itself.) A new meta-type must | ||
| 101 | be defined that makes the type of the class-like objects callable. | ||
| 102 | (Normally, a third type would also be needed, the new ``instance'' | ||
| 103 | type, but this is not an absolute requirement -- the new class type | ||
| 104 | could return an object of some existing type when invoked to create an | ||
| 105 | instance.) | ||
| 106 | |||
| 107 | <P>Still confused? Here's a simple device due to Don himself to | ||
| 108 | explain metaclasses. Take a simple class definition; assume B is a | ||
| 109 | special class that triggers Don's hook: | ||
| 110 | |||
| 111 | <PRE> | ||
| 112 | class C(B): | ||
| 113 | a = 1 | ||
| 114 | b = 2 | ||
| 115 | </PRE> | ||
| 116 | |||
| 117 | This can be though of as equivalent to: | ||
| 118 | |||
| 119 | <PRE> | ||
| 120 | C = type(B)('C', (B,), {'a': 1, 'b': 2}) | ||
| 121 | </PRE> | ||
| 122 | |||
| 123 | If that's too dense for you, here's the same thing written out using | ||
| 124 | temporary variables: | ||
| 125 | |||
| 126 | <PRE> | ||
| 127 | creator = type(B) # The type of the base class | ||
| 128 | name = 'C' # The name of the new class | ||
| 129 | bases = (B,) # A tuple containing the base class(es) | ||
| 130 | namespace = {'a': 1, 'b': 2} # The namespace of the class statement | ||
| 131 | C = creator(name, bases, namespace) | ||
| 132 | </PRE> | ||
| 133 | |||
| 134 | This is analogous to what happens without the Don Beaudry hook, except | ||
| 135 | that in that case the creator function is set to the default class | ||
| 136 | creator. | ||
| 137 | |||
| 138 | <P>In either case, the creator is called with three arguments. The | ||
| 139 | first one, <i>name</i>, is the name of the new class (as given at the | ||
| 140 | top of the class statement). The <i>bases</i> argument is a tuple of | ||
| 141 | base classes (a singleton tuple if there's only one base class, like | ||
| 142 | the example). Finally, <i>namespace</i> is a dictionary containing | ||
| 143 | the local variables collected during execution of the class statement. | ||
| 144 | |||
| 145 | <P>Note that the contents of the namespace dictionary is simply | ||
| 146 | whatever names were defined in the class statement. A little-known | ||
| 147 | fact is that when Python executes a class statement, it enters a new | ||
| 148 | local namespace, and all assignments and function definitions take | ||
| 149 | place in this namespace. Thus, after executing the following class | ||
| 150 | statement: | ||
| 151 | |||
| 152 | <PRE> | ||
| 153 | class C: | ||
| 154 | a = 1 | ||
| 155 | def f(s): pass | ||
| 156 | </PRE> | ||
| 157 | |||
| 158 | the class namespace's contents would be {'a': 1, 'f': <function f | ||
| 159 | ...>}. | ||
| 160 | |||
| 161 | <P>But enough already about writing Python metaclasses in C; read the | ||
| 162 | documentation of <A | ||
| 163 | HREF="http://maigret.cog.brown.edu/pyutil/">MESS</A> or <A | ||
| 164 | HREF="http://www.digicool.com/papers/ExtensionClass.html" >Extension | ||
| 165 | Classes</A> for more information. | ||
| 166 | |||
| 167 | <P> | ||
| 168 | |||
| 169 | <HR> | ||
| 170 | |||
| 171 | <H2>Writing Metaclasses in Python</H2> | ||
| 172 | |||
| 173 | <P>In Python 1.5, the requirement to write a C extension in order to | ||
| 174 | write metaclasses has been dropped (though you can still do | ||
| 175 | it, of course). In addition to the check ``is the type of the base | ||
| 176 | class callable,'' there's a check ``does the base class have a | ||
| 177 | __class__ attribute.'' If so, it is assumed that the __class__ | ||
| 178 | attribute refers to a class. | ||
| 179 | |||
| 180 | <P>Let's repeat our simple example from above: | ||
| 181 | |||
| 182 | <PRE> | ||
| 183 | class C(B): | ||
| 184 | a = 1 | ||
| 185 | b = 2 | ||
| 186 | </PRE> | ||
| 187 | |||
| 188 | Assuming B has a __class__ attribute, this translates into: | ||
| 189 | |||
| 190 | <PRE> | ||
| 191 | C = B.__class__('C', (B,), {'a': 1, 'b': 2}) | ||
| 192 | </PRE> | ||
| 193 | |||
| 194 | This is exactly the same as before except that instead of type(B), | ||
| 195 | B.__class__ is invoked. If you have read <A HREF= | ||
| 196 | "http://www.python.org/cgi-bin/faqw.py?req=show&file=faq06.022.htp" | ||
| 197 | >FAQ question 6.22</A> you will understand that while there is a big | ||
| 198 | technical difference between type(B) and B.__class__, they play the | ||
| 199 | same role at different abstraction levels. And perhaps at some point | ||
| 200 | in the future they will really be the same thing (at which point you | ||
| 201 | would be able to derive subclasses from built-in types). | ||
| 202 | |||
| 203 | <P>At this point it may be worth mentioning that C.__class__ is the | ||
| 204 | same object as B.__class__, i.e., C's metaclass is the same as B's | ||
| 205 | metaclass. In other words, subclassing an existing class creates a | ||
| 206 | new (meta)inststance of the base class's metaclass. | ||
| 207 | |||
| 208 | <P>Going back to the example, the class B.__class__ is instantiated, | ||
| 209 | passing its constructor the same three arguments that are passed to | ||
| 210 | the default class constructor or to an extension's metaclass: | ||
| 211 | <i>name</i>, <i>bases</i>, and <i>namespace</i>. | ||
| 212 | |||
| 213 | <P>It is easy to be confused by what exactly happens when using a | ||
| 214 | metaclass, because we lose the absolute distinction between classes | ||
| 215 | and instances: a class is an instance of a metaclass (a | ||
| 216 | ``metainstance''), but technically (i.e. in the eyes of the python | ||
| 217 | runtime system), the metaclass is just a class, and the metainstance | ||
| 218 | is just an instance. At the end of the class statement, the metaclass | ||
| 219 | whose metainstance is used as a base class is instantiated, yielding a | ||
| 220 | second metainstance (of the same metaclass). This metainstance is | ||
| 221 | then used as a (normal, non-meta) class; instantiation of the class | ||
| 222 | means calling the metainstance, and this will return a real instance. | ||
| 223 | And what class is that an instance of? Conceptually, it is of course | ||
| 224 | an instance of our metainstance; but in most cases the Python runtime | ||
| 225 | system will see it as an instance of a a helper class used by the | ||
| 226 | metaclass to implement its (non-meta) instances... | ||
| 227 | |||
| 228 | <P>Hopefully an example will make things clearer. Let's presume we | ||
| 229 | have a metaclass MetaClass1. It's helper class (for non-meta | ||
| 230 | instances) is callled HelperClass1. We now (manually) instantiate | ||
| 231 | MetaClass1 once to get an empty special base class: | ||
| 232 | |||
| 233 | <PRE> | ||
| 234 | BaseClass1 = MetaClass1("BaseClass1", (), {}) | ||
| 235 | </PRE> | ||
| 236 | |||
| 237 | We can now use BaseClass1 as a base class in a class statement: | ||
| 238 | |||
| 239 | <PRE> | ||
| 240 | class MySpecialClass(BaseClass1): | ||
| 241 | i = 1 | ||
| 242 | def f(s): pass | ||
| 243 | </PRE> | ||
| 244 | |||
| 245 | At this point, MySpecialClass is defined; it is a metainstance of | ||
| 246 | MetaClass1 just like BaseClass1, and in fact the expression | ||
| 247 | ``BaseClass1.__class__ == MySpecialClass.__class__ == MetaClass1'' | ||
| 248 | yields true. | ||
| 249 | |||
| 250 | <P>We are now ready to create instances of MySpecialClass. Let's | ||
| 251 | assume that no constructor arguments are required: | ||
| 252 | |||
| 253 | <PRE> | ||
| 254 | x = MySpecialClass() | ||
| 255 | y = MySpecialClass() | ||
| 256 | print x.__class__, y.__class__ | ||
| 257 | </PRE> | ||
| 258 | |||
| 259 | The print statement shows that x and y are instances of HelperClass1. | ||
| 260 | How did this happen? MySpecialClass is an instance of MetaClass1 | ||
| 261 | (``meta'' is irrelevant here); when an instance is called, its | ||
| 262 | __call__ method is invoked, and presumably the __call__ method defined | ||
| 263 | by MetaClass1 returns an instance of HelperClass1. | ||
| 264 | |||
| 265 | <P>Now let's see how we could use metaclasses -- what can we do | ||
| 266 | with metaclasses that we can't easily do without them? Here's one | ||
| 267 | idea: a metaclass could automatically insert trace calls for all | ||
| 268 | method calls. Let's first develop a simplified example, without | ||
| 269 | support for inheritance or other ``advanced'' Python features (we'll | ||
| 270 | add those later). | ||
| 271 | |||
| 272 | <PRE> | ||
| 273 | import types | ||
| 274 | |||
| 275 | class Tracing: | ||
| 276 | def __init__(self, name, bases, namespace): | ||
| 277 | """Create a new class.""" | ||
| 278 | self.__name__ = name | ||
| 279 | self.__bases__ = bases | ||
| 280 | self.__namespace__ = namespace | ||
| 281 | def __call__(self): | ||
| 282 | """Create a new instance.""" | ||
| 283 | return Instance(self) | ||
| 284 | |||
| 285 | class Instance: | ||
| 286 | def __init__(self, klass): | ||
| 287 | self.__klass__ = klass | ||
| 288 | def __getattr__(self, name): | ||
| 289 | try: | ||
| 290 | value = self.__klass__.__namespace__[name] | ||
| 291 | except KeyError: | ||
| 292 | raise AttributeError, name | ||
| 293 | if type(value) is not types.FunctionType: | ||
| 294 | return value | ||
| 295 | return BoundMethod(value, self) | ||
| 296 | |||
| 297 | class BoundMethod: | ||
| 298 | def __init__(self, function, instance): | ||
| 299 | self.function = function | ||
| 300 | self.instance = instance | ||
| 301 | def __call__(self, *args): | ||
| 302 | print "calling", self.function, "for", self.instance, "with", args | ||
| 303 | return apply(self.function, (self.instance,) + args) | ||
| 304 | |||
| 305 | Trace = Tracing('Trace', (), {}) | ||
| 306 | |||
| 307 | class MyTracedClass(Trace): | ||
| 308 | def method1(self, a): | ||
| 309 | self.a = a | ||
| 310 | def method2(self): | ||
| 311 | return self.a | ||
| 312 | |||
| 313 | aninstance = MyTracedClass() | ||
| 314 | |||
| 315 | aninstance.method1(10) | ||
| 316 | |||
| 317 | print "the answer is %d" % aninstance.method2() | ||
| 318 | </PRE> | ||
| 319 | |||
| 320 | Confused already? The intention is to read this from top down. The | ||
| 321 | Tracing class is the metaclass we're defining. Its structure is | ||
| 322 | really simple. | ||
| 323 | |||
| 324 | <P> | ||
| 325 | |||
| 326 | <UL> | ||
| 327 | |||
| 328 | <LI>The __init__ method is invoked when a new Tracing instance is | ||
| 329 | created, e.g. the definition of class MyTracedClass later in the | ||
| 330 | example. It simply saves the class name, base classes and namespace | ||
| 331 | as instance variables.<P> | ||
| 332 | |||
| 333 | <LI>The __call__ method is invoked when a Tracing instance is called, | ||
| 334 | e.g. the creation of aninstance later in the example. It returns an | ||
| 335 | instance of the class Instance, which is defined next.<P> | ||
| 336 | |||
| 337 | </UL> | ||
| 338 | |||
| 339 | <P>The class Instance is the class used for all instances of classes | ||
| 340 | built using the Tracing metaclass, e.g. aninstance. It has two | ||
| 341 | methods: | ||
| 342 | |||
| 343 | <P> | ||
| 344 | |||
| 345 | <UL> | ||
| 346 | |||
| 347 | <LI>The __init__ method is invoked from the Tracing.__call__ method | ||
| 348 | above to initialize a new instance. It saves the class reference as | ||
| 349 | an instance variable. It uses a funny name because the user's | ||
| 350 | instance variables (e.g. self.a later in the example) live in the same | ||
| 351 | namespace.<P> | ||
| 352 | |||
| 353 | <LI>The __getattr__ method is invoked whenever the user code | ||
| 354 | references an attribute of the instance that is not an instance | ||
| 355 | variable (nor a class variable; but except for __init__ and | ||
| 356 | __getattr__ there are no class variables). It will be called, for | ||
| 357 | example, when aninstance.method1 is referenced in the example, with | ||
| 358 | self set to aninstance and name set to the string "method1".<P> | ||
| 359 | |||
| 360 | </UL> | ||
| 361 | |||
| 362 | <P>The __getattr__ method looks the name up in the __namespace__ | ||
| 363 | dictionary. If it isn't found, it raises an AttributeError exception. | ||
| 364 | (In a more realistic example, it would first have to look through the | ||
| 365 | base classes as well.) If it is found, there are two possibilities: | ||
| 366 | it's either a function or it isn't. If it's not a function, it is | ||
| 367 | assumed to be a class variable, and its value is returned. If it's a | ||
| 368 | function, we have to ``wrap'' it in instance of yet another helper | ||
| 369 | class, BoundMethod. | ||
| 370 | |||
| 371 | <P>The BoundMethod class is needed to implement a familiar feature: | ||
| 372 | when a method is defined, it has an initial argument, self, which is | ||
| 373 | automatically bound to the relevant instance when it is called. For | ||
| 374 | example, aninstance.method1(10) is equivalent to method1(aninstance, | ||
| 375 | 10). In the example if this call, first a temporary BoundMethod | ||
| 376 | instance is created with the following constructor call: temp = | ||
| 377 | BoundMethod(method1, aninstance); then this instance is called as | ||
| 378 | temp(10). After the call, the temporary instance is discarded. | ||
| 379 | |||
| 380 | <P> | ||
| 381 | |||
| 382 | <UL> | ||
| 383 | |||
| 384 | <LI>The __init__ method is invoked for the constructor call | ||
| 385 | BoundMethod(method1, aninstance). It simply saves away its | ||
| 386 | arguments.<P> | ||
| 387 | |||
| 388 | <LI>The __call__ method is invoked when the bound method instance is | ||
| 389 | called, as in temp(10). It needs to call method1(aninstance, 10). | ||
| 390 | However, even though self.function is now method1 and self.instance is | ||
| 391 | aninstance, it can't call self.function(self.instance, args) directly, | ||
| 392 | because it should work regardless of the number of arguments passed. | ||
| 393 | (For simplicity, support for keyword arguments has been omitted.)<P> | ||
| 394 | |||
| 395 | </UL> | ||
| 396 | |||
| 397 | <P>In order to be able to support arbitrary argument lists, the | ||
| 398 | __call__ method first constructs a new argument tuple. Conveniently, | ||
| 399 | because of the notation *args in __call__'s own argument list, the | ||
| 400 | arguments to __call__ (except for self) are placed in the tuple args. | ||
| 401 | To construct the desired argument list, we concatenate a singleton | ||
| 402 | tuple containing the instance with the args tuple: (self.instance,) + | ||
| 403 | args. (Note the trailing comma used to construct the singleton | ||
| 404 | tuple.) In our example, the resulting argument tuple is (aninstance, | ||
| 405 | 10). | ||
| 406 | |||
| 407 | <P>The intrinsic function apply() takes a function and an argument | ||
| 408 | tuple and calls the function for it. In our example, we are calling | ||
| 409 | apply(method1, (aninstance, 10)) which is equivalent to calling | ||
| 410 | method(aninstance, 10). | ||
| 411 | |||
| 412 | <P>From here on, things should come together quite easily. The output | ||
| 413 | of the example code is something like this: | ||
| 414 | |||
| 415 | <PRE> | ||
| 416 | calling <function method1 at ae8d8> for <Instance instance at 95ab0> with (10,) | ||
| 417 | calling <function method2 at ae900> for <Instance instance at 95ab0> with () | ||
| 418 | the answer is 10 | ||
| 419 | </PRE> | ||
| 420 | |||
| 421 | <P>That was about the shortest meaningful example that I could come up | ||
| 422 | with. A real tracing metaclass (for example, <A | ||
| 423 | HREF="#Trace">Trace.py</A> discussed below) needs to be more | ||
| 424 | complicated in two dimensions. | ||
| 425 | |||
| 426 | <P>First, it needs to support more advanced Python features such as | ||
| 427 | class variables, inheritance, __init__ methods, and keyword arguments. | ||
| 428 | |||
| 429 | <P>Second, it needs to provide a more flexible way to handle the | ||
| 430 | actual tracing information; perhaps it should be possible to write | ||
| 431 | your own tracing function that gets called, perhaps it should be | ||
| 432 | possible to enable and disable tracing on a per-class or per-instance | ||
| 433 | basis, and perhaps a filter so that only interesting calls are traced; | ||
| 434 | it should also be able to trace the return value of the call (or the | ||
| 435 | exception it raised if an error occurs). Even the Trace.py example | ||
| 436 | doesn't support all these features yet. | ||
| 437 | |||
| 438 | <P> | ||
| 439 | |||
| 440 | <HR> | ||
| 441 | |||
| 442 | <H1>Real-life Examples</H1> | ||
| 443 | |||
| 444 | <P>Have a look at some very preliminary examples that I coded up to | ||
| 445 | teach myself how to write metaclasses: | ||
| 446 | |||
| 447 | <DL> | ||
| 448 | |||
| 449 | <DT><A HREF="Enum.py">Enum.py</A> | ||
| 450 | |||
| 451 | <DD>This (ab)uses the class syntax as an elegant way to define | ||
| 452 | enumerated types. The resulting classes are never instantiated -- | ||
| 453 | rather, their class attributes are the enumerated values. For | ||
| 454 | example: | ||
| 455 | |||
| 456 | <PRE> | ||
| 457 | class Color(Enum): | ||
| 458 | red = 1 | ||
| 459 | green = 2 | ||
| 460 | blue = 3 | ||
| 461 | print Color.red | ||
| 462 | </PRE> | ||
| 463 | |||
| 464 | will print the string ``Color.red'', while ``Color.red==1'' is true, | ||
| 465 | and ``Color.red + 1'' raise a TypeError exception. | ||
| 466 | |||
| 467 | <P> | ||
| 468 | |||
| 469 | <DT><A NAME=Trace></A><A HREF="Trace.py">Trace.py</A> | ||
| 470 | |||
| 471 | <DD>The resulting classes work much like standard | ||
| 472 | classes, but by setting a special class or instance attribute | ||
| 473 | __trace_output__ to point to a file, all calls to the class's methods | ||
| 474 | are traced. It was a bit of a struggle to get this right. This | ||
| 475 | should probably redone using the generic metaclass below. | ||
| 476 | |||
| 477 | <P> | ||
| 478 | |||
| 479 | <DT><A HREF="Meta.py">Meta.py</A> | ||
| 480 | |||
| 481 | <DD>A generic metaclass. This is an attempt at finding out how much | ||
| 482 | standard class behavior can be mimicked by a metaclass. The | ||
| 483 | preliminary answer appears to be that everything's fine as long as the | ||
| 484 | class (or its clients) don't look at the instance's __class__ | ||
| 485 | attribute, nor at the class's __dict__ attribute. The use of | ||
| 486 | __getattr__ internally makes the classic implementation of __getattr__ | ||
| 487 | hooks tough; we provide a similar hook _getattr_ instead. | ||
| 488 | (__setattr__ and __delattr__ are not affected.) | ||
| 489 | (XXX Hm. Could detect presence of __getattr__ and rename it.) | ||
| 490 | |||
| 491 | <P> | ||
| 492 | |||
| 493 | <DT><A HREF="Eiffel.py">Eiffel.py</A> | ||
| 494 | |||
| 495 | <DD>Uses the above generic metaclass to implement Eiffel style | ||
| 496 | pre-conditions and post-conditions. | ||
| 497 | |||
| 498 | <P> | ||
| 499 | |||
| 500 | <DT><A HREF="Synch.py">Synch.py</A> | ||
| 501 | |||
| 502 | <DD>Uses the above generic metaclass to implement synchronized | ||
| 503 | methods. | ||
| 504 | |||
| 505 | <P> | ||
| 506 | |||
| 507 | <DT><A HREF="Simple.py">Simple.py</A> | ||
| 508 | |||
| 509 | <DD>The example module used above. | ||
| 510 | |||
| 511 | <P> | ||
| 512 | |||
| 513 | </DL> | ||
| 514 | |||
| 515 | <P>A pattern seems to be emerging: almost all these uses of | ||
| 516 | metaclasses (except for Enum, which is probably more cute than useful) | ||
| 517 | mostly work by placing wrappers around method calls. An obvious | ||
| 518 | problem with that is that it's not easy to combine the features of | ||
| 519 | different metaclasses, while this would actually be quite useful: for | ||
| 520 | example, I wouldn't mind getting a trace from the test run of the | ||
| 521 | Synch module, and it would be interesting to add preconditions to it | ||
| 522 | as well. This needs more research. Perhaps a metaclass could be | ||
| 523 | provided that allows stackable wrappers... | ||
| 524 | |||
| 525 | <P> | ||
| 526 | |||
| 527 | <HR> | ||
| 528 | |||
| 529 | <H2>Things You Could Do With Metaclasses</H2> | ||
| 530 | |||
| 531 | <P>There are lots of things you could do with metaclasses. Most of | ||
| 532 | these can also be done with creative use of __getattr__, but | ||
| 533 | metaclasses make it easier to modify the attribute lookup behavior of | ||
| 534 | classes. Here's a partial list. | ||
| 535 | |||
| 536 | <P> | ||
| 537 | |||
| 538 | <UL> | ||
| 539 | |||
| 540 | <LI>Enforce different inheritance semantics, e.g. automatically call | ||
| 541 | base class methods when a derived class overrides<P> | ||
| 542 | |||
| 543 | <LI>Implement class methods (e.g. if the first argument is not named | ||
| 544 | 'self')<P> | ||
| 545 | |||
| 546 | <LI>Implement that each instance is initialized with <b>copies</b> of | ||
| 547 | all class variables<P> | ||
| 548 | |||
| 549 | <LI>Implement a different way to store instance variables (e.g. in a | ||
| 550 | list kept outside the instance but indexed by the instance's id())<P> | ||
| 551 | |||
| 552 | <LI>Automatically wrap or trap all or certain methods | ||
| 553 | |||
| 554 | <UL> | ||
| 555 | |||
| 556 | <LI>for tracing | ||
| 557 | |||
| 558 | <LI>for precondition and postcondition checking | ||
| 559 | |||
| 560 | <LI>for synchronized methods | ||
| 561 | |||
| 562 | <LI>for automatic value caching | ||
| 563 | |||
| 564 | </UL> | ||
| 565 | <P> | ||
| 566 | |||
| 567 | <LI>When an attribute is a parameterless function, call it on | ||
| 568 | reference (to mimic it being an instance variable); same on assignment<P> | ||
| 569 | |||
| 570 | <LI>Instrumentation: see how many times various attributes are used<P> | ||
| 571 | |||
| 572 | <LI>Different semantics for __setattr__ and __getattr__ (e.g. disable | ||
| 573 | them when they are being used recursively)<P> | ||
| 574 | |||
| 575 | <LI>Abuse class syntax for other things<P> | ||
| 576 | |||
| 577 | <LI>Experiment with automatic type checking<P> | ||
| 578 | |||
| 579 | <LI>Delegation (or acquisition)<P> | ||
| 580 | |||
| 581 | <LI>Dynamic inheritance patterns<P> | ||
| 582 | |||
| 583 | <LI>Automatic caching of methods<P> | ||
| 584 | |||
| 585 | </UL> | ||
| 586 | |||
| 587 | <P> | ||
| 588 | |||
| 589 | <HR> | ||
| 590 | |||
| 591 | <H4>Credits</H4> | ||
| 592 | |||
| 593 | <P>Many thanks to David Ascher and Donald Beaudry for their comments | ||
| 594 | on earlier draft of this paper. Also thanks to Matt Conway and Tommy | ||
| 595 | Burnette for putting a seed for the idea of metaclasses in my | ||
| 596 | mind, nearly three years ago, even though at the time my response was | ||
| 597 | ``you can do that with __getattr__ hooks...'' :-) | ||
| 598 | |||
| 599 | <P> | ||
| 600 | |||
| 601 | <HR> | ||
| 602 | |||
| 603 | </BODY> | ||
| 604 | |||
| 605 | </HTML> | ||
| @@ -0,0 +1,256 @@ | |||
| 1 | Subject: Re: The metaclass saga using Python | ||
| 2 | From: Vladimir Marangozov <Vladimir.Marangozov@imag.fr> | ||
| 3 | To: tim_one@email.msn.com (Tim Peters) | ||
| 4 | Cc: python-list@cwi.nl | ||
| 5 | Date: Wed, 5 Aug 1998 15:59:06 +0200 (DFT) | ||
| 6 | |||
| 7 | [Tim] | ||
| 8 | > | ||
| 9 | > building-on-examples-tends-to-prevent-abstract-thrashing-ly y'rs - tim | ||
| 10 | > | ||
| 11 | |||
| 12 | OK, I stand corrected. I understand that anybody's interpretation of | ||
| 13 | the meta-class concept is likely to be difficult to digest by others. | ||
| 14 | |||
| 15 | Here's another try, expressing the same thing, but using the Python | ||
| 16 | programming model, examples and, perhaps, more popular terms. | ||
| 17 | |||
| 18 | 1. Classes. | ||
| 19 | |||
| 20 | This is pure Python of today. Sorry about the tutorial, but it is | ||
| 21 | meant to illustrate the second part, which is the one we're | ||
| 22 | interested in and which will follow the same development scenario. | ||
| 23 | Besides, newbies are likely to understand that the discussion is | ||
| 24 | affordable even for them :-) | ||
| 25 | |||
| 26 | a) Class definition | ||
| 27 | |||
| 28 | A class is meant to define the common properties of a set of objects. | ||
| 29 | A class is a "package" of properties. The assembly of properties | ||
| 30 | in a class package is sometimes called a class structure (which isn't | ||
| 31 | always appropriate). | ||
| 32 | |||
| 33 | >>> class A: | ||
| 34 | attr1 = "Hello" # an attribute of A | ||
| 35 | def method1(self, *args): pass # method1 of A | ||
| 36 | def method2(self, *args): pass # method2 of A | ||
| 37 | >>> | ||
| 38 | |||
| 39 | So far, we defined the structure of the class A. The class A is | ||
| 40 | of type <class>. We can check this by asking Python: "what is A?" | ||
| 41 | |||
| 42 | >>> A # What is A? | ||
| 43 | <class __main__.A at 2023e360> | ||
| 44 | |||
| 45 | b) Class instantiation | ||
| 46 | |||
| 47 | Creating an object with the properties defined in the class A is | ||
| 48 | called instantiation of the class A. After an instantiation of A, we | ||
| 49 | obtain a new object, called an instance, which has the properties | ||
| 50 | packaged in the class A. | ||
| 51 | |||
| 52 | >>> a = A() # 'a' is the 1st instance of A | ||
| 53 | >>> a # What is 'a'? | ||
| 54 | <__main__.A instance at 2022b9d0> | ||
| 55 | |||
| 56 | >>> b = A() # 'b' is another instance of A | ||
| 57 | >>> b # What is 'b'? | ||
| 58 | <__main__.A instance at 2022b9c0> | ||
| 59 | |||
| 60 | The objects, 'a' and 'b', are of type <instance> and they both have | ||
| 61 | the same properties. Note, that 'a' and 'b' are different objects. | ||
| 62 | (their adresses differ). This is a bit hard to see, so let's ask Python: | ||
| 63 | |||
| 64 | >>> a == b # Is 'a' the same object as 'b'? | ||
| 65 | 0 # No. | ||
| 66 | |||
| 67 | Instance objects have one more special property, indicating the class | ||
| 68 | they are an instance of. This property is named __class__. | ||
| 69 | |||
| 70 | >>> a.__class__ # What is the class of 'a'? | ||
| 71 | <class __main__.A at 2023e360> # 'a' is an instance of A | ||
| 72 | >>> b.__class__ # What is the class of 'b'? | ||
| 73 | <class __main__.A at 2023e360> # 'b' is an instance of A | ||
| 74 | >>> a.__class__ == b.__class__ # Is it really the same class A? | ||
| 75 | 1 # Yes. | ||
| 76 | |||
| 77 | c) Class inheritance (class composition and specialization) | ||
| 78 | |||
| 79 | Classes can be defined in terms of other existing classes (and only | ||
| 80 | classes! -- don't bug me on this now). Thus, we can compose property | ||
| 81 | packages and create new ones. We reuse the property set defined | ||
| 82 | in a class by defining a new class, which "inherits" from the former. | ||
| 83 | In other words, a class B which inherits from the class A, inherits | ||
| 84 | the properties defined in A, or, B inherits the structure of A. | ||
| 85 | |||
| 86 | In the same time, at the definition of the new class B, we can enrich | ||
| 87 | the inherited set of properties by adding new ones and/or modify some | ||
| 88 | of the inherited properties. | ||
| 89 | |||
| 90 | >>> class B(A): # B inherits A's properties | ||
| 91 | attr2 = "World" # additional attr2 | ||
| 92 | def method2(self, arg1): pass # method2 is redefined | ||
| 93 | def method3(self, *args): pass # additional method3 | ||
| 94 | |||
| 95 | >>> B # What is B? | ||
| 96 | <class __main__.B at 2023e500> | ||
| 97 | >>> B == A # Is B the same class as A? | ||
| 98 | 0 # No. | ||
| 99 | |||
| 100 | Classes define one special property, indicating whether a class | ||
| 101 | inherits the properties of another class. This property is called | ||
| 102 | __bases__ and it contains a list (a tuple) of the classes the new | ||
| 103 | class inherits from. The classes from which a class is inheriting the | ||
| 104 | properties are called superclasses (in Python, we call them also -- | ||
| 105 | base classes). | ||
| 106 | |||
| 107 | >>> A.__bases__ # Does A have any superclasses? | ||
| 108 | () # No. | ||
| 109 | >>> B.__bases__ # Does B have any superclasses? | ||
| 110 | (<class __main__.A at 2023e360>,) # Yes. It has one superclass. | ||
| 111 | >>> B.__bases__[0] == A # Is it really the class A? | ||
| 112 | 1 # Yes, it is. | ||
| 113 | |||
| 114 | -------- | ||
| 115 | |||
| 116 | Congratulations on getting this far! This was the hard part. | ||
| 117 | Now, let's continue with the easy one. | ||
| 118 | |||
| 119 | -------- | ||
| 120 | |||
| 121 | 2. Meta-classes | ||
| 122 | |||
| 123 | You have to admit, that an anonymous group of Python wizards are | ||
| 124 | not satisfied with the property packaging facilities presented above. | ||
| 125 | They say, that the Real-World bugs them with problems that cannot be | ||
| 126 | modelled successfully with classes. Or, that the way classes are | ||
| 127 | implemented in Python and the way classes and instances behave at | ||
| 128 | runtime isn't always appropriate for reproducing the Real-World's | ||
| 129 | behavior in a way that satisfies them. | ||
| 130 | |||
| 131 | Hence, what they want is the following: | ||
| 132 | |||
| 133 | a) leave objects as they are (instances of classes) | ||
| 134 | b) leave classes as they are (property packages and object creators) | ||
| 135 | |||
| 136 | BUT, at the same time: | ||
| 137 | |||
| 138 | c) consider classes as being instances of mysterious objects. | ||
| 139 | d) label mysterious objects "meta-classes". | ||
| 140 | |||
| 141 | Easy, eh? | ||
| 142 | |||
| 143 | You may ask: "Why on earth do they want to do that?". | ||
| 144 | They answer: "Poor soul... Go and see how cruel the Real-World is!". | ||
| 145 | You - fuzzy: "OK, will do!" | ||
| 146 | |||
| 147 | And here we go for another round of what I said in section 1 -- Classes. | ||
| 148 | |||
| 149 | However, be warned! The features we're going to talk about aren't fully | ||
| 150 | implemented yet, because the Real-World don't let wizards to evaluate | ||
| 151 | precisely how cruel it is, so the features are still highly-experimental. | ||
| 152 | |||
| 153 | a) Meta-class definition | ||
| 154 | |||
| 155 | A meta-class is meant to define the common properties of a set of | ||
| 156 | classes. A meta-class is a "package" of properties. The assembly | ||
| 157 | of properties in a meta-class package is sometimes called a meta-class | ||
| 158 | structure (which isn't always appropriate). | ||
| 159 | |||
| 160 | In Python, a meta-class definition would have looked like this: | ||
| 161 | |||
| 162 | >>> metaclass M: | ||
| 163 | attr1 = "Hello" # an attribute of M | ||
| 164 | def method1(self, *args): pass # method1 of M | ||
| 165 | def method2(self, *args): pass # method2 of M | ||
| 166 | >>> | ||
| 167 | |||
| 168 | So far, we defined the structure of the meta-class M. The meta-class | ||
| 169 | M is of type <metaclass>. We cannot check this by asking Python, but | ||
| 170 | if we could, it would have answered: | ||
| 171 | |||
| 172 | >>> M # What is M? | ||
| 173 | <metaclass __main__.M at 2023e4e0> | ||
| 174 | |||
| 175 | b) Meta-class instantiation | ||
| 176 | |||
| 177 | Creating an object with the properties defined in the meta-class M is | ||
| 178 | called instantiation of the meta-class M. After an instantiation of M, | ||
| 179 | we obtain a new object, called an class, but now it is called also | ||
| 180 | a meta-instance, which has the properties packaged in the meta-class M. | ||
| 181 | |||
| 182 | In Python, instantiating a meta-class would have looked like this: | ||
| 183 | |||
| 184 | >>> A = M() # 'A' is the 1st instance of M | ||
| 185 | >>> A # What is 'A'? | ||
| 186 | <class __main__.A at 2022b9d0> | ||
| 187 | |||
| 188 | >>> B = M() # 'B' is another instance of M | ||
| 189 | >>> B # What is 'B'? | ||
| 190 | <class __main__.B at 2022b9c0> | ||
| 191 | |||
| 192 | The metaclass-instances, A and B, are of type <class> and they both | ||
| 193 | have the same properties. Note, that A and B are different objects. | ||
| 194 | (their adresses differ). This is a bit hard to see, but if it was | ||
| 195 | possible to ask Python, it would have answered: | ||
| 196 | |||
| 197 | >>> A == B # Is A the same class as B? | ||
| 198 | 0 # No. | ||
| 199 | |||
| 200 | Class objects have one more special property, indicating the meta-class | ||
| 201 | they are an instance of. This property is named __metaclass__. | ||
| 202 | |||
| 203 | >>> A.__metaclass__ # What is the meta-class of A? | ||
| 204 | <metaclass __main__.M at 2023e4e0> # A is an instance of M | ||
| 205 | >>> A.__metaclass__ # What is the meta-class of B? | ||
| 206 | <metaclass __main__.M at 2023e4e0> # B is an instance of M | ||
| 207 | >>> A.__metaclass__ == B.__metaclass__ # Is it the same meta-class M? | ||
| 208 | 1 # Yes. | ||
| 209 | |||
| 210 | c) Meta-class inheritance (meta-class composition and specialization) | ||
| 211 | |||
| 212 | Meta-classes can be defined in terms of other existing meta-classes | ||
| 213 | (and only meta-classes!). Thus, we can compose property packages and | ||
| 214 | create new ones. We reuse the property set defined in a meta-class by | ||
| 215 | defining a new meta-class, which "inherits" from the former. | ||
| 216 | In other words, a meta-class N which inherits from the meta-class M, | ||
| 217 | inherits the properties defined in M, or, N inherits the structure of M. | ||
| 218 | |||
| 219 | In the same time, at the definition of the new meta-class N, we can | ||
| 220 | enrich the inherited set of properties by adding new ones and/or modify | ||
| 221 | some of the inherited properties. | ||
| 222 | |||
| 223 | >>> metaclass N(M): # N inherits M's properties | ||
| 224 | attr2 = "World" # additional attr2 | ||
| 225 | def method2(self, arg1): pass # method2 is redefined | ||
| 226 | def method3(self, *args): pass # additional method3 | ||
| 227 | |||
| 228 | >>> N # What is N? | ||
| 229 | <metaclass __main__.N at 2023e500> | ||
| 230 | >>> N == M # Is N the same meta-class as M? | ||
| 231 | 0 # No. | ||
| 232 | |||
| 233 | Meta-classes define one special property, indicating whether a | ||
| 234 | meta-class inherits the properties of another meta-class. This property | ||
| 235 | is called __metabases__ and it contains a list (a tuple) of the | ||
| 236 | meta-classes the new meta-class inherits from. The meta-classes from | ||
| 237 | which a meta-class is inheriting the properties are called | ||
| 238 | super-meta-classes (in Python, we call them also -- super meta-bases). | ||
| 239 | |||
| 240 | >>> M.__metabases__ # Does M have any supermetaclasses? | ||
| 241 | () # No. | ||
| 242 | >>> N.__metabases__ # Does N have any supermetaclasses? | ||
| 243 | (<metaclass __main__.M at 2023e360>,) # Yes. It has a supermetaclass. | ||
| 244 | >>> N.__metabases__[0] == M # Is it really the meta-class M? | ||
| 245 | 1 # Yes, it is. | ||
| 246 | |||
| 247 | -------- | ||
| 248 | |||
| 249 | Triple congratulations on getting this far! | ||
| 250 | Now you know everything about meta-classes and the Real-World! | ||
| 251 | |||
| 252 | <unless-wizards-want-meta-classes-be-instances-of-mysterious-objects!> | ||
| 253 | |||
| 254 | -- | ||
| 255 | Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr | ||
| 256 | http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252 | ||
| @@ -0,0 +1,141 @@ | |||
| 1 | """Support Eiffel-style preconditions and postconditions.""" | ||
| 2 | |||
| 3 | from new import function | ||
| 4 | |||
| 5 | class EiffelBaseMetaClass(type): | ||
| 6 | |||
| 7 | def __new__(meta, name, bases, dict): | ||
| 8 | meta.convert_methods(dict) | ||
| 9 | return super(EiffelBaseMetaClass, meta).__new__(meta, name, bases, | ||
| 10 | dict) | ||
| 11 | |||
| 12 | @classmethod | ||
| 13 | def convert_methods(cls, dict): | ||
| 14 | """Replace functions in dict with EiffelMethod wrappers. | ||
| 15 | |||
| 16 | The dict is modified in place. | ||
| 17 | |||
| 18 | If a method ends in _pre or _post, it is removed from the dict | ||
| 19 | regardless of whether there is a corresponding method. | ||
| 20 | """ | ||
| 21 | # find methods with pre or post conditions | ||
| 22 | methods = [] | ||
| 23 | for k, v in dict.iteritems(): | ||
| 24 | if k.endswith('_pre') or k.endswith('_post'): | ||
| 25 | assert isinstance(v, function) | ||
| 26 | elif isinstance(v, function): | ||
| 27 | methods.append(k) | ||
| 28 | for m in methods: | ||
| 29 | pre = dict.get("%s_pre" % m) | ||
| 30 | post = dict.get("%s_post" % m) | ||
| 31 | if pre or post: | ||
| 32 | dict[k] = cls.make_eiffel_method(dict[m], pre, post) | ||
| 33 | |||
| 34 | class EiffelMetaClass1(EiffelBaseMetaClass): | ||
| 35 | # an implementation of the "eiffel" meta class that uses nested functions | ||
| 36 | |||
| 37 | @staticmethod | ||
| 38 | def make_eiffel_method(func, pre, post): | ||
| 39 | def method(self, *args, **kwargs): | ||
| 40 | if pre: | ||
| 41 | pre(self, *args, **kwargs) | ||
| 42 | x = func(self, *args, **kwargs) | ||
| 43 | if post: | ||
| 44 | post(self, x, *args, **kwargs) | ||
| 45 | return x | ||
| 46 | |||
| 47 | if func.__doc__: | ||
| 48 | method.__doc__ = func.__doc__ | ||
| 49 | |||
| 50 | return method | ||
| 51 | |||
| 52 | class EiffelMethodWrapper: | ||
| 53 | |||
| 54 | def __init__(self, inst, descr): | ||
| 55 | self._inst = inst | ||
| 56 | self._descr = descr | ||
| 57 | |||
| 58 | def __call__(self, *args, **kwargs): | ||
| 59 | return self._descr.callmethod(self._inst, args, kwargs) | ||
| 60 | |||
| 61 | class EiffelDescriptor(object): | ||
| 62 | |||
| 63 | def __init__(self, func, pre, post): | ||
| 64 | self._func = func | ||
| 65 | self._pre = pre | ||
| 66 | self._post = post | ||
| 67 | |||
| 68 | self.__name__ = func.__name__ | ||
| 69 | self.__doc__ = func.__doc__ | ||
| 70 | |||
| 71 | def __get__(self, obj, cls): | ||
| 72 | return EiffelMethodWrapper(obj, self) | ||
| 73 | |||
| 74 | def callmethod(self, inst, args, kwargs): | ||
| 75 | if self._pre: | ||
| 76 | self._pre(inst, *args, **kwargs) | ||
| 77 | x = self._func(inst, *args, **kwargs) | ||
| 78 | if self._post: | ||
| 79 | self._post(inst, x, *args, **kwargs) | ||
| 80 | return x | ||
| 81 | |||
| 82 | class EiffelMetaClass2(EiffelBaseMetaClass): | ||
| 83 | # an implementation of the "eiffel" meta class that uses descriptors | ||
| 84 | |||
| 85 | make_eiffel_method = EiffelDescriptor | ||
| 86 | |||
| 87 | def _test(metaclass): | ||
| 88 | class Eiffel: | ||
| 89 | __metaclass__ = metaclass | ||
| 90 | |||
| 91 | class Test(Eiffel): | ||
| 92 | |||
| 93 | def m(self, arg): | ||
| 94 | """Make it a little larger""" | ||
| 95 | return arg + 1 | ||
| 96 | |||
| 97 | def m2(self, arg): | ||
| 98 | """Make it a little larger""" | ||
| 99 | return arg + 1 | ||
| 100 | |||
| 101 | def m2_pre(self, arg): | ||
| 102 | assert arg > 0 | ||
| 103 | |||
| 104 | def m2_post(self, result, arg): | ||
| 105 | assert result > arg | ||
| 106 | |||
| 107 | class Sub(Test): | ||
| 108 | def m2(self, arg): | ||
| 109 | return arg**2 | ||
| 110 | def m2_post(self, Result, arg): | ||
| 111 | super(Sub, self).m2_post(Result, arg) | ||
| 112 | assert Result < 100 | ||
| 113 | |||
| 114 | t = Test() | ||
| 115 | t.m(1) | ||
| 116 | t.m2(1) | ||
| 117 | try: | ||
| 118 | t.m2(0) | ||
| 119 | except AssertionError: | ||
| 120 | pass | ||
| 121 | else: | ||
| 122 | assert False | ||
| 123 | |||
| 124 | s = Sub() | ||
| 125 | try: | ||
| 126 | s.m2(1) | ||
| 127 | except AssertionError: | ||
| 128 | pass # result == arg | ||
| 129 | else: | ||
| 130 | assert False | ||
| 131 | try: | ||
| 132 | s.m2(10) | ||
| 133 | except AssertionError: | ||
| 134 | pass # result == 100 | ||
| 135 | else: | ||
| 136 | assert False | ||
| 137 | s.m2(5) | ||
| 138 | |||
| 139 | if __name__ == "__main__": | ||
| 140 | _test(EiffelMetaClass1) | ||
| 141 | _test(EiffelMetaClass2) | ||
| @@ -0,0 +1,177 @@ | |||
| 1 | """Enumeration metaclass.""" | ||
| 2 | |||
| 3 | class EnumMetaclass(type): | ||
| 4 | """Metaclass for enumeration. | ||
| 5 | |||
| 6 | To define your own enumeration, do something like | ||
| 7 | |||
| 8 | class Color(Enum): | ||
| 9 | red = 1 | ||
| 10 | green = 2 | ||
| 11 | blue = 3 | ||
| 12 | |||
| 13 | Now, Color.red, Color.green and Color.blue behave totally | ||
| 14 | different: they are enumerated values, not integers. | ||
| 15 | |||
| 16 | Enumerations cannot be instantiated; however they can be | ||
| 17 | subclassed. | ||
| 18 | """ | ||
| 19 | |||
| 20 | def __init__(cls, name, bases, dict): | ||
| 21 | super(EnumMetaclass, cls).__init__(name, bases, dict) | ||
| 22 | cls._members = [] | ||
| 23 | for attr in dict.keys(): | ||
| 24 | if not (attr.startswith('__') and attr.endswith('__')): | ||
| 25 | enumval = EnumInstance(name, attr, dict[attr]) | ||
| 26 | setattr(cls, attr, enumval) | ||
| 27 | cls._members.append(attr) | ||
| 28 | |||
| 29 | def __getattr__(cls, name): | ||
| 30 | if name == "__members__": | ||
| 31 | return cls._members | ||
| 32 | raise AttributeError, name | ||
| 33 | |||
| 34 | def __repr__(cls): | ||
| 35 | s1 = s2 = "" | ||
| 36 | enumbases = [base.__name__ for base in cls.__bases__ | ||
| 37 | if isinstance(base, EnumMetaclass) and not base is Enum] | ||
| 38 | if enumbases: | ||
| 39 | s1 = "(%s)" % ", ".join(enumbases) | ||
| 40 | enumvalues = ["%s: %d" % (val, getattr(cls, val)) | ||
| 41 | for val in cls._members] | ||
| 42 | if enumvalues: | ||
| 43 | s2 = ": {%s}" % ", ".join(enumvalues) | ||
| 44 | return "%s%s%s" % (cls.__name__, s1, s2) | ||
| 45 | |||
| 46 | class FullEnumMetaclass(EnumMetaclass): | ||
| 47 | """Metaclass for full enumerations. | ||
| 48 | |||
| 49 | A full enumeration displays all the values defined in base classes. | ||
| 50 | """ | ||
| 51 | |||
| 52 | def __init__(cls, name, bases, dict): | ||
| 53 | super(FullEnumMetaclass, cls).__init__(name, bases, dict) | ||
| 54 | for obj in cls.__mro__: | ||
| 55 | if isinstance(obj, EnumMetaclass): | ||
| 56 | for attr in obj._members: | ||
| 57 | # XXX inefficient | ||
| 58 | if not attr in cls._members: | ||
| 59 | cls._members.append(attr) | ||
| 60 | |||
| 61 | class EnumInstance(int): | ||
| 62 | """Class to represent an enumeration value. | ||
| 63 | |||
| 64 | EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves | ||
| 65 | like the integer 12 when compared, but doesn't support arithmetic. | ||
| 66 | |||
| 67 | XXX Should it record the actual enumeration rather than just its | ||
| 68 | name? | ||
| 69 | """ | ||
| 70 | |||
| 71 | def __new__(cls, classname, enumname, value): | ||
| 72 | return int.__new__(cls, value) | ||
| 73 | |||
| 74 | def __init__(self, classname, enumname, value): | ||
| 75 | self.__classname = classname | ||
| 76 | self.__enumname = enumname | ||
| 77 | |||
| 78 | def __repr__(self): | ||
| 79 | return "EnumInstance(%s, %s, %d)" % (self.__classname, self.__enumname, | ||
| 80 | self) | ||
| 81 | |||
| 82 | def __str__(self): | ||
| 83 | return "%s.%s" % (self.__classname, self.__enumname) | ||
| 84 | |||
| 85 | class Enum: | ||
| 86 | __metaclass__ = EnumMetaclass | ||
| 87 | |||
| 88 | class FullEnum: | ||
| 89 | __metaclass__ = FullEnumMetaclass | ||
| 90 | |||
| 91 | def _test(): | ||
| 92 | |||
| 93 | class Color(Enum): | ||
| 94 | red = 1 | ||
| 95 | green = 2 | ||
| 96 | blue = 3 | ||
| 97 | |||
| 98 | print Color.red | ||
| 99 | |||
| 100 | print repr(Color.red) | ||
| 101 | print Color.red == Color.red | ||
| 102 | print Color.red == Color.blue | ||
| 103 | print Color.red == 1 | ||
| 104 | print Color.red == 2 | ||
| 105 | |||
| 106 | class ExtendedColor(Color): | ||
| 107 | white = 0 | ||
| 108 | orange = 4 | ||
| 109 | yellow = 5 | ||
| 110 | purple = 6 | ||
| 111 | black = 7 | ||
| 112 | |||
| 113 | print ExtendedColor.orange | ||
| 114 | print ExtendedColor.red | ||
| 115 | |||
| 116 | print Color.red == ExtendedColor.red | ||
| 117 | |||
| 118 | class OtherColor(Enum): | ||
| 119 | white = 4 | ||
| 120 | blue = 5 | ||
| 121 | |||
| 122 | class MergedColor(Color, OtherColor): | ||
| 123 | pass | ||
| 124 | |||
| 125 | print MergedColor.red | ||
| 126 | print MergedColor.white | ||
| 127 | |||
| 128 | print Color | ||
| 129 | print ExtendedColor | ||
| 130 | print OtherColor | ||
| 131 | print MergedColor | ||
| 132 | |||
| 133 | def _test2(): | ||
| 134 | |||
| 135 | class Color(FullEnum): | ||
| 136 | red = 1 | ||
| 137 | green = 2 | ||
| 138 | blue = 3 | ||
| 139 | |||
| 140 | print Color.red | ||
| 141 | |||
| 142 | print repr(Color.red) | ||
| 143 | print Color.red == Color.red | ||
| 144 | print Color.red == Color.blue | ||
| 145 | print Color.red == 1 | ||
| 146 | print Color.red == 2 | ||
| 147 | |||
| 148 | class ExtendedColor(Color): | ||
| 149 | white = 0 | ||
| 150 | orange = 4 | ||
| 151 | yellow = 5 | ||
| 152 | purple = 6 | ||
| 153 | black = 7 | ||
| 154 | |||
| 155 | print ExtendedColor.orange | ||
| 156 | print ExtendedColor.red | ||
| 157 | |||
| 158 | print Color.red == ExtendedColor.red | ||
| 159 | |||
| 160 | class OtherColor(FullEnum): | ||
| 161 | white = 4 | ||
| 162 | blue = 5 | ||
| 163 | |||
| 164 | class MergedColor(Color, OtherColor): | ||
| 165 | pass | ||
| 166 | |||
| 167 | print MergedColor.red | ||
| 168 | print MergedColor.white | ||
| 169 | |||
| 170 | print Color | ||
| 171 | print ExtendedColor | ||
| 172 | print OtherColor | ||
| 173 | print MergedColor | ||
| 174 | |||
| 175 | if __name__ == '__main__': | ||
| 176 | _test() | ||
| 177 | _test2() | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | Demo/parser | ||
| 2 | Doc/libparser.tex | ||
| 3 | Lib/AST.py | ||
| 4 | Lib/symbol.py | ||
| 5 | Lib/token.py | ||
| 6 | Modules/parsermodule.c | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | These files are from the large example of using the `parser' module. Refer | ||
| 2 | to the Python Library Reference for more information. | ||
| 3 | |||
| 4 | It also contains examples for the AST parser. | ||
| 5 | |||
| 6 | Files: | ||
| 7 | ------ | ||
| 8 | |||
| 9 | FILES -- list of files associated with the parser module. | ||
| 10 | |||
| 11 | README -- this file. | ||
| 12 | |||
| 13 | example.py -- module that uses the `parser' module to extract | ||
| 14 | information from the parse tree of Python source | ||
| 15 | code. | ||
| 16 | |||
| 17 | docstring.py -- sample source file containing only a module docstring. | ||
| 18 | |||
| 19 | simple.py -- sample source containing a "short form" definition. | ||
| 20 | |||
| 21 | source.py -- sample source code used to demonstrate ability to | ||
| 22 | handle nested constructs easily using the functions | ||
| 23 | and classes in example.py. | ||
| 24 | |||
| 25 | test_parser.py program to put the parser module through its paces. | ||
| 26 | |||
| 27 | unparse.py AST (2.5) based example to recreate source code | ||
| 28 | from an AST. This is incomplete; contributions | ||
| 29 | are welcome. | ||
| 30 | |||
| 31 | Enjoy! | ||
| @@ -0,0 +1,2 @@ | |||
| 1 | """Some documentation. | ||
| 2 | """ | ||
| @@ -0,0 +1,190 @@ | |||
| 1 | """Simple code to extract class & function docstrings from a module. | ||
| 2 | |||
| 3 | This code is used as an example in the library reference manual in the | ||
| 4 | section on using the parser module. Refer to the manual for a thorough | ||
| 5 | discussion of the operation of this code. | ||
| 6 | """ | ||
| 7 | |||
| 8 | import os | ||
| 9 | import parser | ||
| 10 | import symbol | ||
| 11 | import token | ||
| 12 | import types | ||
| 13 | |||
| 14 | from types import ListType, TupleType | ||
| 15 | |||
| 16 | |||
| 17 | def get_docs(fileName): | ||
| 18 | """Retrieve information from the parse tree of a source file. | ||
| 19 | |||
| 20 | fileName | ||
| 21 | Name of the file to read Python source code from. | ||
| 22 | """ | ||
| 23 | source = open(fileName).read() | ||
| 24 | basename = os.path.basename(os.path.splitext(fileName)[0]) | ||
| 25 | ast = parser.suite(source) | ||
| 26 | return ModuleInfo(ast.totuple(), basename) | ||
| 27 | |||
| 28 | |||
| 29 | class SuiteInfoBase: | ||
| 30 | _docstring = '' | ||
| 31 | _name = '' | ||
| 32 | |||
| 33 | def __init__(self, tree = None): | ||
| 34 | self._class_info = {} | ||
| 35 | self._function_info = {} | ||
| 36 | if tree: | ||
| 37 | self._extract_info(tree) | ||
| 38 | |||
| 39 | def _extract_info(self, tree): | ||
| 40 | # extract docstring | ||
| 41 | if len(tree) == 2: | ||
| 42 | found, vars = match(DOCSTRING_STMT_PATTERN[1], tree[1]) | ||
| 43 | else: | ||
| 44 | found, vars = match(DOCSTRING_STMT_PATTERN, tree[3]) | ||
| 45 | if found: | ||
| 46 | self._docstring = eval(vars['docstring']) | ||
| 47 | # discover inner definitions | ||
| 48 | for node in tree[1:]: | ||
| 49 | found, vars = match(COMPOUND_STMT_PATTERN, node) | ||
| 50 | if found: | ||
| 51 | cstmt = vars['compound'] | ||
| 52 | if cstmt[0] == symbol.funcdef: | ||
| 53 | name = cstmt[2][1] | ||
| 54 | self._function_info[name] = FunctionInfo(cstmt) | ||
| 55 | elif cstmt[0] == symbol.classdef: | ||
| 56 | name = cstmt[2][1] | ||
| 57 | self._class_info[name] = ClassInfo(cstmt) | ||
| 58 | |||
| 59 | def get_docstring(self): | ||
| 60 | return self._docstring | ||
| 61 | |||
| 62 | def get_name(self): | ||
| 63 | return self._name | ||
| 64 | |||
| 65 | def get_class_names(self): | ||
| 66 | return self._class_info.keys() | ||
| 67 | |||
| 68 | def get_class_info(self, name): | ||
| 69 | return self._class_info[name] | ||
| 70 | |||
| 71 | def __getitem__(self, name): | ||
| 72 | try: | ||
| 73 | return self._class_info[name] | ||
| 74 | except KeyError: | ||
| 75 | return self._function_info[name] | ||
| 76 | |||
| 77 | |||
| 78 | class SuiteFuncInfo: | ||
| 79 | # Mixin class providing access to function names and info. | ||
| 80 | |||
| 81 | def get_function_names(self): | ||
| 82 | return self._function_info.keys() | ||
| 83 | |||
| 84 | def get_function_info(self, name): | ||
| 85 | return self._function_info[name] | ||
| 86 | |||
| 87 | |||
| 88 | class FunctionInfo(SuiteInfoBase, SuiteFuncInfo): | ||
| 89 | def __init__(self, tree = None): | ||
| 90 | self._name = tree[2][1] | ||
| 91 | SuiteInfoBase.__init__(self, tree and tree[-1] or None) | ||
| 92 | |||
| 93 | |||
| 94 | class ClassInfo(SuiteInfoBase): | ||
| 95 | def __init__(self, tree = None): | ||
| 96 | self._name = tree[2][1] | ||
| 97 | SuiteInfoBase.__init__(self, tree and tree[-1] or None) | ||
| 98 | |||
| 99 | def get_method_names(self): | ||
| 100 | return self._function_info.keys() | ||
| 101 | |||
| 102 | def get_method_info(self, name): | ||
| 103 | return self._function_info[name] | ||
| 104 | |||
| 105 | |||
| 106 | class ModuleInfo(SuiteInfoBase, SuiteFuncInfo): | ||
| 107 | def __init__(self, tree = None, name = "<string>"): | ||
| 108 | self._name = name | ||
| 109 | SuiteInfoBase.__init__(self, tree) | ||
| 110 | if tree: | ||
| 111 | found, vars = match(DOCSTRING_STMT_PATTERN, tree[1]) | ||
| 112 | if found: | ||
| 113 | self._docstring = vars["docstring"] | ||
| 114 | |||
| 115 | |||
| 116 | def match(pattern, data, vars=None): | ||
| 117 | """Match `data' to `pattern', with variable extraction. | ||
| 118 | |||
| 119 | pattern | ||
| 120 | Pattern to match against, possibly containing variables. | ||
| 121 | |||
| 122 | data | ||
| 123 | Data to be checked and against which variables are extracted. | ||
| 124 | |||
| 125 | vars | ||
| 126 | Dictionary of variables which have already been found. If not | ||
| 127 | provided, an empty dictionary is created. | ||
| 128 | |||
| 129 | The `pattern' value may contain variables of the form ['varname'] which | ||
| 130 | are allowed to match anything. The value that is matched is returned as | ||
| 131 | part of a dictionary which maps 'varname' to the matched value. 'varname' | ||
| 132 | is not required to be a string object, but using strings makes patterns | ||
| 133 | and the code which uses them more readable. | ||
| 134 | |||
| 135 | This function returns two values: a boolean indicating whether a match | ||
| 136 | was found and a dictionary mapping variable names to their associated | ||
| 137 | values. | ||
| 138 | """ | ||
| 139 | if vars is None: | ||
| 140 | vars = {} | ||
| 141 | if type(pattern) is ListType: # 'variables' are ['varname'] | ||
| 142 | vars[pattern[0]] = data | ||
| 143 | return 1, vars | ||
| 144 | if type(pattern) is not TupleType: | ||
| 145 | return (pattern == data), vars | ||
| 146 | if len(data) != len(pattern): | ||
| 147 | return 0, vars | ||
| 148 | for pattern, data in map(None, pattern, data): | ||
| 149 | same, vars = match(pattern, data, vars) | ||
| 150 | if not same: | ||
| 151 | break | ||
| 152 | return same, vars | ||
| 153 | |||
| 154 | |||
| 155 | # This pattern identifies compound statements, allowing them to be readily | ||
| 156 | # differentiated from simple statements. | ||
| 157 | # | ||
| 158 | COMPOUND_STMT_PATTERN = ( | ||
| 159 | symbol.stmt, | ||
| 160 | (symbol.compound_stmt, ['compound']) | ||
| 161 | ) | ||
| 162 | |||
| 163 | |||
| 164 | # This pattern will match a 'stmt' node which *might* represent a docstring; | ||
| 165 | # docstrings require that the statement which provides the docstring be the | ||
| 166 | # first statement in the class or function, which this pattern does not check. | ||
| 167 | # | ||
| 168 | DOCSTRING_STMT_PATTERN = ( | ||
| 169 | symbol.stmt, | ||
| 170 | (symbol.simple_stmt, | ||
| 171 | (symbol.small_stmt, | ||
| 172 | (symbol.expr_stmt, | ||
| 173 | (symbol.testlist, | ||
| 174 | (symbol.test, | ||
| 175 | (symbol.and_test, | ||
| 176 | (symbol.not_test, | ||
| 177 | (symbol.comparison, | ||
| 178 | (symbol.expr, | ||
| 179 | (symbol.xor_expr, | ||
| 180 | (symbol.and_expr, | ||
| 181 | (symbol.shift_expr, | ||
| 182 | (symbol.arith_expr, | ||
| 183 | (symbol.term, | ||
| 184 | (symbol.factor, | ||
| 185 | (symbol.power, | ||
| 186 | (symbol.atom, | ||
| 187 | (token.STRING, ['docstring']) | ||
| 188 | )))))))))))))))), | ||
| 189 | (token.NEWLINE, '') | ||
| 190 | )) | ||
| @@ -0,0 +1 @@ | |||
| 1 | def f(): "maybe a docstring" | ||
| @@ -0,0 +1,27 @@ | |||
| 1 | """Exmaple file to be parsed for the parsermodule example. | ||
| 2 | |||
| 3 | The classes and functions in this module exist only to exhibit the ability | ||
| 4 | of the handling information extraction from nested definitions using parse | ||
| 5 | trees. They shouldn't interest you otherwise! | ||
| 6 | """ | ||
| 7 | |||
| 8 | class Simple: | ||
| 9 | "This class does very little." | ||
| 10 | |||
| 11 | def method(self): | ||
| 12 | "This method does almost nothing." | ||
| 13 | return 1 | ||
| 14 | |||
| 15 | class Nested: | ||
| 16 | "This is a nested class." | ||
| 17 | |||
| 18 | def nested_method(self): | ||
| 19 | "Method of Nested class." | ||
| 20 | def nested_function(): | ||
| 21 | "Function in method of Nested class." | ||
| 22 | pass | ||
| 23 | return nested_function | ||
| 24 | |||
| 25 | def function(): | ||
| 26 | "This function lives at the module level." | ||
| 27 | return 0 | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | # (Force the script to use the latest build.) | ||
| 3 | # | ||
| 4 | # test_parser.py | ||
| 5 | |||
| 6 | import parser, traceback | ||
| 7 | |||
| 8 | _numFailed = 0 | ||
| 9 | |||
| 10 | def testChunk(t, fileName): | ||
| 11 | global _numFailed | ||
| 12 | print '----', fileName, | ||
| 13 | try: | ||
| 14 | ast = parser.suite(t) | ||
| 15 | tup = parser.ast2tuple(ast) | ||
| 16 | # this discards the first AST; a huge memory savings when running | ||
| 17 | # against a large source file like Tkinter.py. | ||
| 18 | ast = None | ||
| 19 | new = parser.tuple2ast(tup) | ||
| 20 | except parser.ParserError, err: | ||
| 21 | |||
| 22 | print 'parser module raised exception on input file', fileName + ':' | ||
| 23 | traceback.print_exc() | ||
| 24 | _numFailed = _numFailed + 1 | ||
| 25 | else: | ||
| 26 | if tup != parser.ast2tuple(new): | ||
| 27 | |||
| 28 | print 'parser module failed on input file', fileName | ||
| 29 | _numFailed = _numFailed + 1 | ||
| 30 | else: | ||
| 31 | print 'o.k.' | ||
| 32 | |||
| 33 | def testFile(fileName): | ||
| 34 | t = open(fileName).read() | ||
| 35 | testChunk(t, fileName) | ||
| 36 | |||
| 37 | def test(): | ||
| 38 | import sys | ||
| 39 | args = sys.argv[1:] | ||
| 40 | if not args: | ||
| 41 | import glob | ||
| 42 | args = glob.glob("*.py") | ||
| 43 | args.sort() | ||
| 44 | map(testFile, args) | ||
| 45 | sys.exit(_numFailed != 0) | ||
| 46 | |||
| 47 | if __name__ == '__main__': | ||
| 48 | test() | ||
| @@ -0,0 +1,100 @@ | |||
| 1 | \input texinfo @c -*-texinfo-*- | ||
| 2 | @c %**start of header | ||
| 3 | @setfilename parser.info | ||
| 4 | @settitle Python Parser Module Reference | ||
| 5 | @setchapternewpage odd | ||
| 6 | @footnotestyle end | ||
| 7 | @c %**end of header | ||
| 8 | |||
| 9 | @ifinfo | ||
| 10 | This file describes the interfaces | ||
| 11 | published by the optional @code{parser} module and gives examples of | ||
| 12 | how they may be used. It contains the same text as the chapter on the | ||
| 13 | @code{parser} module in the @cite{Python Library Reference}, but is | ||
| 14 | presented as a separate document. | ||
| 15 | |||
| 16 | Copyright 1995-1996 by Fred L. Drake, Jr., Reston, Virginia, USA, and | ||
| 17 | Virginia Polytechnic Institute and State University, Blacksburg, | ||
| 18 | Virginia, USA. Portions of the software copyright 1991-1995 by | ||
| 19 | Stichting Mathematisch Centrum, Amsterdam, The Netherlands. Copying is | ||
| 20 | permitted under the terms associated with the main Python distribution, | ||
| 21 | with the additional restriction that this additional notice be included | ||
| 22 | and maintained on all distributed copies. | ||
| 23 | |||
| 24 | All Rights Reserved | ||
| 25 | |||
| 26 | Permission to use, copy, modify, and distribute this software and its | ||
| 27 | documentation for any purpose and without fee is hereby granted, | ||
| 28 | provided that the above copyright notice appear in all copies and that | ||
| 29 | both that copyright notice and this permission notice appear in | ||
| 30 | supporting documentation, and that the names of Fred L. Drake, Jr. and | ||
| 31 | Virginia Polytechnic Institute and State University not be used in | ||
| 32 | advertising or publicity pertaining to distribution of the software | ||
| 33 | without specific, written prior permission. | ||
| 34 | |||
| 35 | FRED L. DRAKE, JR. AND VIRGINIA POLYTECHNIC INSTITUTE AND STATE | ||
| 36 | UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
| 37 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
| 38 | EVENT SHALL FRED L. DRAKE, JR. OR VIRGINIA POLYTECHNIC INSTITUTE AND | ||
| 39 | STATE UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL | ||
| 40 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | ||
| 41 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
| 42 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
| 43 | PERFORMANCE OF THIS SOFTWARE. | ||
| 44 | @end ifinfo | ||
| 45 | |||
| 46 | @titlepage | ||
| 47 | @title Python Parser Module Reference | ||
| 48 | @author Fred L. Drake, Jr. | ||
| 49 | |||
| 50 | @c The following two commands start the copyright page. | ||
| 51 | @page | ||
| 52 | @vskip 0pt plus 1filll | ||
| 53 | Copyright 1995-1996 by Fred L. Drake, Jr., Reston, Virginia, USA, and | ||
| 54 | Virginia Polytechnic Institute and State University, Blacksburg, | ||
| 55 | Virginia, USA. Portions of the software copyright 1991-1995 by | ||
| 56 | Stichting Mathematisch Centrum, Amsterdam, The Netherlands. Copying is | ||
| 57 | permitted under the terms associated with the main Python distribution, | ||
| 58 | with the additional restriction that this additional notice be included | ||
| 59 | and maintained on all distributed copies. | ||
| 60 | |||
| 61 | @center All Rights Reserved | ||
| 62 | |||
| 63 | Permission to use, copy, modify, and distribute this software and its | ||
| 64 | documentation for any purpose and without fee is hereby granted, | ||
| 65 | provided that the above copyright notice appear in all copies and that | ||
| 66 | both that copyright notice and this permission notice appear in | ||
| 67 | supporting documentation, and that the names of Fred L. Drake, Jr. and | ||
| 68 | Virginia Polytechnic Institute and State University not be used in | ||
| 69 | advertising or publicity pertaining to distribution of the software | ||
| 70 | without specific, written prior permission. | ||
| 71 | |||
| 72 | FRED L. DRAKE, JR. AND VIRGINIA POLYTECHNIC INSTITUTE AND STATE | ||
| 73 | UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
| 74 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
| 75 | EVENT SHALL FRED L. DRAKE, JR. OR VIRGINIA POLYTECHNIC INSTITUTE AND | ||
| 76 | STATE UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL | ||
| 77 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | ||
| 78 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
| 79 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
| 80 | PERFORMANCE OF THIS SOFTWARE. | ||
| 81 | @end titlepage | ||
| 82 | |||
| 83 | |||
| 84 | @node Top, Overview, (dir), (dir) | ||
| 85 | @top The Python Parser Module | ||
| 86 | |||
| 87 | @ifinfo | ||
| 88 | This file describes the interfaces | ||
| 89 | published by the optional @code{parser} module and gives examples of | ||
| 90 | how they may be used. It contains the same text as the chapter on the | ||
| 91 | @code{parser} module in the @cite{Python Library Reference}, but is | ||
| 92 | presented as a separate document. | ||
| 93 | |||
| 94 | This version corresponds to Python version 1.4 (1 Sept. 1996). | ||
| 95 | |||
| 96 | @end ifinfo | ||
| 97 | |||
| 98 | @c placeholder for the master menu -- patched by texinfo-all-menus-update | ||
| 99 | @menu | ||
| 100 | @end menu | ||
| @@ -0,0 +1,519 @@ | |||
| 1 | "Usage: unparse.py <path to source file>" | ||
| 2 | import sys | ||
| 3 | import _ast | ||
| 4 | import cStringIO | ||
| 5 | import os | ||
| 6 | |||
| 7 | class Unparser: | ||
| 8 | """Methods in this class recursively traverse an AST and | ||
| 9 | output source code for the abstract syntax; original formatting | ||
| 10 | is disregarged. """ | ||
| 11 | |||
| 12 | def __init__(self, tree, file = sys.stdout): | ||
| 13 | """Unparser(tree, file=sys.stdout) -> None. | ||
| 14 | Print the source for tree to file.""" | ||
| 15 | self.f = file | ||
| 16 | self._indent = 0 | ||
| 17 | self.dispatch(tree) | ||
| 18 | print >>self.f,"" | ||
| 19 | self.f.flush() | ||
| 20 | |||
| 21 | def fill(self, text = ""): | ||
| 22 | "Indent a piece of text, according to the current indentation level" | ||
| 23 | self.f.write("\n"+" "*self._indent + text) | ||
| 24 | |||
| 25 | def write(self, text): | ||
| 26 | "Append a piece of text to the current line." | ||
| 27 | self.f.write(text) | ||
| 28 | |||
| 29 | def enter(self): | ||
| 30 | "Print ':', and increase the indentation." | ||
| 31 | self.write(":") | ||
| 32 | self._indent += 1 | ||
| 33 | |||
| 34 | def leave(self): | ||
| 35 | "Decrease the indentation level." | ||
| 36 | self._indent -= 1 | ||
| 37 | |||
| 38 | def dispatch(self, tree): | ||
| 39 | "Dispatcher function, dispatching tree type T to method _T." | ||
| 40 | if isinstance(tree, list): | ||
| 41 | for t in tree: | ||
| 42 | self.dispatch(t) | ||
| 43 | return | ||
| 44 | meth = getattr(self, "_"+tree.__class__.__name__) | ||
| 45 | meth(tree) | ||
| 46 | |||
| 47 | |||
| 48 | ############### Unparsing methods ###################### | ||
| 49 | # There should be one method per concrete grammar type # | ||
| 50 | # Constructors should be grouped by sum type. Ideally, # | ||
| 51 | # this would follow the order in the grammar, but # | ||
| 52 | # currently doesn't. # | ||
| 53 | ######################################################## | ||
| 54 | |||
| 55 | def _Module(self, tree): | ||
| 56 | for stmt in tree.body: | ||
| 57 | self.dispatch(stmt) | ||
| 58 | |||
| 59 | # stmt | ||
| 60 | def _Expr(self, tree): | ||
| 61 | self.fill() | ||
| 62 | self.dispatch(tree.value) | ||
| 63 | |||
| 64 | def _Import(self, t): | ||
| 65 | self.fill("import ") | ||
| 66 | first = True | ||
| 67 | for a in t.names: | ||
| 68 | if first: | ||
| 69 | first = False | ||
| 70 | else: | ||
| 71 | self.write(", ") | ||
| 72 | self.write(a.name) | ||
| 73 | if a.asname: | ||
| 74 | self.write(" as "+a.asname) | ||
| 75 | |||
| 76 | def _ImportFrom(self, t): | ||
| 77 | self.fill("from ") | ||
| 78 | self.write(t.module) | ||
| 79 | self.write(" import ") | ||
| 80 | for i, a in enumerate(t.names): | ||
| 81 | if i == 0: | ||
| 82 | self.write(", ") | ||
| 83 | self.write(a.name) | ||
| 84 | if a.asname: | ||
| 85 | self.write(" as "+a.asname) | ||
| 86 | # XXX(jpe) what is level for? | ||
| 87 | |||
| 88 | def _Assign(self, t): | ||
| 89 | self.fill() | ||
| 90 | for target in t.targets: | ||
| 91 | self.dispatch(target) | ||
| 92 | self.write(" = ") | ||
| 93 | self.dispatch(t.value) | ||
| 94 | |||
| 95 | def _AugAssign(self, t): | ||
| 96 | self.fill() | ||
| 97 | self.dispatch(t.target) | ||
| 98 | self.write(" "+self.binop[t.op.__class__.__name__]+"= ") | ||
| 99 | self.dispatch(t.value) | ||
| 100 | |||
| 101 | def _Return(self, t): | ||
| 102 | self.fill("return ") | ||
| 103 | if t.value: | ||
| 104 | self.dispatch(t.value) | ||
| 105 | |||
| 106 | def _Pass(self, t): | ||
| 107 | self.fill("pass") | ||
| 108 | |||
| 109 | def _Break(self, t): | ||
| 110 | self.fill("break") | ||
| 111 | |||
| 112 | def _Continue(self, t): | ||
| 113 | self.fill("continue") | ||
| 114 | |||
| 115 | def _Delete(self, t): | ||
| 116 | self.fill("del ") | ||
| 117 | self.dispatch(t.targets) | ||
| 118 | |||
| 119 | def _Assert(self, t): | ||
| 120 | self.fill("assert ") | ||
| 121 | self.dispatch(t.test) | ||
| 122 | if t.msg: | ||
| 123 | self.write(", ") | ||
| 124 | self.dispatch(t.msg) | ||
| 125 | |||
| 126 | def _Exec(self, t): | ||
| 127 | self.fill("exec ") | ||
| 128 | self.dispatch(t.body) | ||
| 129 | if t.globals: | ||
| 130 | self.write(" in ") | ||
| 131 | self.dispatch(t.globals) | ||
| 132 | if t.locals: | ||
| 133 | self.write(", ") | ||
| 134 | self.dispatch(t.locals) | ||
| 135 | |||
| 136 | def _Print(self, t): | ||
| 137 | self.fill("print ") | ||
| 138 | do_comma = False | ||
| 139 | if t.dest: | ||
| 140 | self.write(">>") | ||
| 141 | self.dispatch(t.dest) | ||
| 142 | do_comma = True | ||
| 143 | for e in t.values: | ||
| 144 | if do_comma:self.write(", ") | ||
| 145 | else:do_comma=True | ||
| 146 | self.dispatch(e) | ||
| 147 | if not t.nl: | ||
| 148 | self.write(",") | ||
| 149 | |||
| 150 | def _Global(self, t): | ||
| 151 | self.fill("global") | ||
| 152 | for i, n in enumerate(t.names): | ||
| 153 | if i != 0: | ||
| 154 | self.write(",") | ||
| 155 | self.write(" " + n) | ||
| 156 | |||
| 157 | def _Yield(self, t): | ||
| 158 | self.fill("yield") | ||
| 159 | if t.value: | ||
| 160 | self.write(" (") | ||
| 161 | self.dispatch(t.value) | ||
| 162 | self.write(")") | ||
| 163 | |||
| 164 | def _Raise(self, t): | ||
| 165 | self.fill('raise ') | ||
| 166 | if t.type: | ||
| 167 | self.dispatch(t.type) | ||
| 168 | if t.inst: | ||
| 169 | self.write(", ") | ||
| 170 | self.dispatch(t.inst) | ||
| 171 | if t.tback: | ||
| 172 | self.write(", ") | ||
| 173 | self.dispatch(t.tback) | ||
| 174 | |||
| 175 | def _TryExcept(self, t): | ||
| 176 | self.fill("try") | ||
| 177 | self.enter() | ||
| 178 | self.dispatch(t.body) | ||
| 179 | self.leave() | ||
| 180 | |||
| 181 | for ex in t.handlers: | ||
| 182 | self.dispatch(ex) | ||
| 183 | if t.orelse: | ||
| 184 | self.fill("else") | ||
| 185 | self.enter() | ||
| 186 | self.dispatch(t.orelse) | ||
| 187 | self.leave() | ||
| 188 | |||
| 189 | def _TryFinally(self, t): | ||
| 190 | self.fill("try") | ||
| 191 | self.enter() | ||
| 192 | self.dispatch(t.body) | ||
| 193 | self.leave() | ||
| 194 | |||
| 195 | self.fill("finally") | ||
| 196 | self.enter() | ||
| 197 | self.dispatch(t.finalbody) | ||
| 198 | self.leave() | ||
| 199 | |||
| 200 | def _excepthandler(self, t): | ||
| 201 | self.fill("except ") | ||
| 202 | if t.type: | ||
| 203 | self.dispatch(t.type) | ||
| 204 | if t.name: | ||
| 205 | self.write(", ") | ||
| 206 | self.dispatch(t.name) | ||
| 207 | self.enter() | ||
| 208 | self.dispatch(t.body) | ||
| 209 | self.leave() | ||
| 210 | |||
| 211 | def _ClassDef(self, t): | ||
| 212 | self.write("\n") | ||
| 213 | self.fill("class "+t.name) | ||
| 214 | if t.bases: | ||
| 215 | self.write("(") | ||
| 216 | for a in t.bases: | ||
| 217 | self.dispatch(a) | ||
| 218 | self.write(", ") | ||
| 219 | self.write(")") | ||
| 220 | self.enter() | ||
| 221 | self.dispatch(t.body) | ||
| 222 | self.leave() | ||
| 223 | |||
| 224 | def _FunctionDef(self, t): | ||
| 225 | self.write("\n") | ||
| 226 | for deco in t.decorators: | ||
| 227 | self.fill("@") | ||
| 228 | self.dispatch(deco) | ||
| 229 | self.fill("def "+t.name + "(") | ||
| 230 | self.dispatch(t.args) | ||
| 231 | self.write(")") | ||
| 232 | self.enter() | ||
| 233 | self.dispatch(t.body) | ||
| 234 | self.leave() | ||
| 235 | |||
| 236 | def _For(self, t): | ||
| 237 | self.fill("for ") | ||
| 238 | self.dispatch(t.target) | ||
| 239 | self.write(" in ") | ||
| 240 | self.dispatch(t.iter) | ||
| 241 | self.enter() | ||
| 242 | self.dispatch(t.body) | ||
| 243 | self.leave() | ||
| 244 | if t.orelse: | ||
| 245 | self.fill("else") | ||
| 246 | self.enter() | ||
| 247 | self.dispatch(t.orelse) | ||
| 248 | self.leave | ||
| 249 | |||
| 250 | def _If(self, t): | ||
| 251 | self.fill("if ") | ||
| 252 | self.dispatch(t.test) | ||
| 253 | self.enter() | ||
| 254 | # XXX elif? | ||
| 255 | self.dispatch(t.body) | ||
| 256 | self.leave() | ||
| 257 | if t.orelse: | ||
| 258 | self.fill("else") | ||
| 259 | self.enter() | ||
| 260 | self.dispatch(t.orelse) | ||
| 261 | self.leave() | ||
| 262 | |||
| 263 | def _While(self, t): | ||
| 264 | self.fill("while ") | ||
| 265 | self.dispatch(t.test) | ||
| 266 | self.enter() | ||
| 267 | self.dispatch(t.body) | ||
| 268 | self.leave() | ||
| 269 | if t.orelse: | ||
| 270 | self.fill("else") | ||
| 271 | self.enter() | ||
| 272 | self.dispatch(t.orelse) | ||
| 273 | self.leave | ||
| 274 | |||
| 275 | def _With(self, t): | ||
| 276 | self.fill("with ") | ||
| 277 | self.dispatch(t.context_expr) | ||
| 278 | if t.optional_vars: | ||
| 279 | self.write(" as ") | ||
| 280 | self.dispatch(t.optional_vars) | ||
| 281 | self.enter() | ||
| 282 | self.dispatch(t.body) | ||
| 283 | self.leave() | ||
| 284 | |||
| 285 | # expr | ||
| 286 | def _Str(self, tree): | ||
| 287 | self.write(repr(tree.s)) | ||
| 288 | |||
| 289 | def _Name(self, t): | ||
| 290 | self.write(t.id) | ||
| 291 | |||
| 292 | def _Repr(self, t): | ||
| 293 | self.write("`") | ||
| 294 | self.dispatch(t.value) | ||
| 295 | self.write("`") | ||
| 296 | |||
| 297 | def _Num(self, t): | ||
| 298 | self.write(repr(t.n)) | ||
| 299 | |||
| 300 | def _List(self, t): | ||
| 301 | self.write("[") | ||
| 302 | for e in t.elts: | ||
| 303 | self.dispatch(e) | ||
| 304 | self.write(", ") | ||
| 305 | self.write("]") | ||
| 306 | |||
| 307 | def _ListComp(self, t): | ||
| 308 | self.write("[") | ||
| 309 | self.dispatch(t.elt) | ||
| 310 | for gen in t.generators: | ||
| 311 | self.dispatch(gen) | ||
| 312 | self.write("]") | ||
| 313 | |||
| 314 | def _GeneratorExp(self, t): | ||
| 315 | self.write("(") | ||
| 316 | self.dispatch(t.elt) | ||
| 317 | for gen in t.generators: | ||
| 318 | self.dispatch(gen) | ||
| 319 | self.write(")") | ||
| 320 | |||
| 321 | def _comprehension(self, t): | ||
| 322 | self.write(" for ") | ||
| 323 | self.dispatch(t.target) | ||
| 324 | self.write(" in ") | ||
| 325 | self.dispatch(t.iter) | ||
| 326 | for if_clause in t.ifs: | ||
| 327 | self.write(" if ") | ||
| 328 | self.dispatch(if_clause) | ||
| 329 | |||
| 330 | def _IfExp(self, t): | ||
| 331 | self.dispatch(t.body) | ||
| 332 | self.write(" if ") | ||
| 333 | self.dispatch(t.test) | ||
| 334 | if t.orelse: | ||
| 335 | self.write(" else ") | ||
| 336 | self.dispatch(t.orelse) | ||
| 337 | |||
| 338 | def _Dict(self, t): | ||
| 339 | self.write("{") | ||
| 340 | for k,v in zip(t.keys, t.values): | ||
| 341 | self.dispatch(k) | ||
| 342 | self.write(" : ") | ||
| 343 | self.dispatch(v) | ||
| 344 | self.write(", ") | ||
| 345 | self.write("}") | ||
| 346 | |||
| 347 | def _Tuple(self, t): | ||
| 348 | if not t.elts: | ||
| 349 | self.write("()") | ||
| 350 | return | ||
| 351 | self.write("(") | ||
| 352 | for e in t.elts: | ||
| 353 | self.dispatch(e) | ||
| 354 | self.write(", ") | ||
| 355 | self.write(")") | ||
| 356 | |||
| 357 | unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"} | ||
| 358 | def _UnaryOp(self, t): | ||
| 359 | self.write(self.unop[t.op.__class__.__name__]) | ||
| 360 | self.write("(") | ||
| 361 | self.dispatch(t.operand) | ||
| 362 | self.write(")") | ||
| 363 | |||
| 364 | binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%", | ||
| 365 | "LShift":">>", "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&", | ||
| 366 | "FloorDiv":"//", "Pow": "**"} | ||
| 367 | def _BinOp(self, t): | ||
| 368 | self.write("(") | ||
| 369 | self.dispatch(t.left) | ||
| 370 | self.write(")" + self.binop[t.op.__class__.__name__] + "(") | ||
| 371 | self.dispatch(t.right) | ||
| 372 | self.write(")") | ||
| 373 | |||
| 374 | cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=", | ||
| 375 | "Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"} | ||
| 376 | def _Compare(self, t): | ||
| 377 | self.write("(") | ||
| 378 | self.dispatch(t.left) | ||
| 379 | for o, e in zip(t.ops, t.comparators): | ||
| 380 | self.write(") " +self.cmpops[o.__class__.__name__] + " (") | ||
| 381 | self.dispatch(e) | ||
| 382 | self.write(")") | ||
| 383 | |||
| 384 | boolops = {_ast.And: 'and', _ast.Or: 'or'} | ||
| 385 | def _BoolOp(self, t): | ||
| 386 | self.write("(") | ||
| 387 | self.dispatch(t.values[0]) | ||
| 388 | for v in t.values[1:]: | ||
| 389 | self.write(" %s " % self.boolops[t.op.__class__]) | ||
| 390 | self.dispatch(v) | ||
| 391 | self.write(")") | ||
| 392 | |||
| 393 | def _Attribute(self,t): | ||
| 394 | self.dispatch(t.value) | ||
| 395 | self.write(".") | ||
| 396 | self.write(t.attr) | ||
| 397 | |||
| 398 | def _Call(self, t): | ||
| 399 | self.dispatch(t.func) | ||
| 400 | self.write("(") | ||
| 401 | comma = False | ||
| 402 | for e in t.args: | ||
| 403 | if comma: self.write(", ") | ||
| 404 | else: comma = True | ||
| 405 | self.dispatch(e) | ||
| 406 | for e in t.keywords: | ||
| 407 | if comma: self.write(", ") | ||
| 408 | else: comma = True | ||
| 409 | self.dispatch(e) | ||
| 410 | if t.starargs: | ||
| 411 | if comma: self.write(", ") | ||
| 412 | else: comma = True | ||
| 413 | self.write("*") | ||
| 414 | self.dispatch(t.starargs) | ||
| 415 | if t.kwargs: | ||
| 416 | if comma: self.write(", ") | ||
| 417 | else: comma = True | ||
| 418 | self.write("**") | ||
| 419 | self.dispatch(t.kwargs) | ||
| 420 | self.write(")") | ||
| 421 | |||
| 422 | def _Subscript(self, t): | ||
| 423 | self.dispatch(t.value) | ||
| 424 | self.write("[") | ||
| 425 | self.dispatch(t.slice) | ||
| 426 | self.write("]") | ||
| 427 | |||
| 428 | # slice | ||
| 429 | def _Ellipsis(self, t): | ||
| 430 | self.write("...") | ||
| 431 | |||
| 432 | def _Index(self, t): | ||
| 433 | self.dispatch(t.value) | ||
| 434 | |||
| 435 | def _Slice(self, t): | ||
| 436 | if t.lower: | ||
| 437 | self.dispatch(t.lower) | ||
| 438 | self.write(":") | ||
| 439 | if t.upper: | ||
| 440 | self.dispatch(t.upper) | ||
| 441 | if t.step: | ||
| 442 | self.write(":") | ||
| 443 | self.dispatch(t.step) | ||
| 444 | |||
| 445 | def _ExtSlice(self, t): | ||
| 446 | for i, d in enumerate(t.dims): | ||
| 447 | if i != 0: | ||
| 448 | self.write(': ') | ||
| 449 | self.dispatch(d) | ||
| 450 | |||
| 451 | # others | ||
| 452 | def _arguments(self, t): | ||
| 453 | first = True | ||
| 454 | nonDef = len(t.args)-len(t.defaults) | ||
| 455 | for a in t.args[0:nonDef]: | ||
| 456 | if first:first = False | ||
| 457 | else: self.write(", ") | ||
| 458 | self.dispatch(a) | ||
| 459 | for a,d in zip(t.args[nonDef:], t.defaults): | ||
| 460 | if first:first = False | ||
| 461 | else: self.write(", ") | ||
| 462 | self.dispatch(a), | ||
| 463 | self.write("=") | ||
| 464 | self.dispatch(d) | ||
| 465 | if t.vararg: | ||
| 466 | if first:first = False | ||
| 467 | else: self.write(", ") | ||
| 468 | self.write("*"+t.vararg) | ||
| 469 | if t.kwarg: | ||
| 470 | if first:first = False | ||
| 471 | else: self.write(", ") | ||
| 472 | self.write("**"+t.kwarg) | ||
| 473 | |||
| 474 | def _keyword(self, t): | ||
| 475 | self.write(t.arg) | ||
| 476 | self.write("=") | ||
| 477 | self.dispatch(t.value) | ||
| 478 | |||
| 479 | def _Lambda(self, t): | ||
| 480 | self.write("lambda ") | ||
| 481 | self.dispatch(t.args) | ||
| 482 | self.write(": ") | ||
| 483 | self.dispatch(t.body) | ||
| 484 | |||
| 485 | def roundtrip(filename, output=sys.stdout): | ||
| 486 | source = open(filename).read() | ||
| 487 | tree = compile(source, filename, "exec", 0x400) | ||
| 488 | Unparser(tree, output) | ||
| 489 | |||
| 490 | |||
| 491 | |||
| 492 | def testdir(a): | ||
| 493 | try: | ||
| 494 | names = [n for n in os.listdir(a) if n.endswith('.py')] | ||
| 495 | except OSError: | ||
| 496 | print >> sys.stderr, "Directory not readable: %s" % a | ||
| 497 | else: | ||
| 498 | for n in names: | ||
| 499 | fullname = os.path.join(a, n) | ||
| 500 | if os.path.isfile(fullname): | ||
| 501 | output = cStringIO.StringIO() | ||
| 502 | print 'Testing %s' % fullname | ||
| 503 | try: | ||
| 504 | roundtrip(fullname, output) | ||
| 505 | except Exception, e: | ||
| 506 | print ' Failed to compile, exception is %s' % repr(e) | ||
| 507 | elif os.path.isdir(fullname): | ||
| 508 | testdir(fullname) | ||
| 509 | |||
| 510 | def main(args): | ||
| 511 | if args[0] == '--testdir': | ||
| 512 | for a in args[1:]: | ||
| 513 | testdir(a) | ||
| 514 | else: | ||
| 515 | for a in args: | ||
| 516 | roundtrip(a) | ||
| 517 | |||
| 518 | if __name__=='__main__': | ||
| 519 | main(sys.argv[1:]) | ||
| @@ -0,0 +1,322 @@ | |||
| 1 | """File System Proxy. | ||
| 2 | |||
| 3 | Provide an OS-neutral view on a file system, locally or remotely. | ||
| 4 | The functionality is geared towards implementing some sort of | ||
| 5 | rdist-like utility between a Mac and a UNIX system. | ||
| 6 | |||
| 7 | The module defines three classes: | ||
| 8 | |||
| 9 | FSProxyLocal -- used for local access | ||
| 10 | FSProxyServer -- used on the server side of remote access | ||
| 11 | FSProxyClient -- used on the client side of remote access | ||
| 12 | |||
| 13 | The remote classes are instantiated with an IP address and an optional | ||
| 14 | verbosity flag. | ||
| 15 | """ | ||
| 16 | |||
| 17 | import server | ||
| 18 | import client | ||
| 19 | import md5 | ||
| 20 | import os | ||
| 21 | import fnmatch | ||
| 22 | from stat import * | ||
| 23 | import time | ||
| 24 | import fnmatch | ||
| 25 | |||
| 26 | if os.name == 'mac': | ||
| 27 | import macfs | ||
| 28 | maxnamelen = 31 | ||
| 29 | else: | ||
| 30 | macfs = None | ||
| 31 | maxnamelen = 255 | ||
| 32 | |||
| 33 | skipnames = (os.curdir, os.pardir) | ||
| 34 | |||
| 35 | |||
| 36 | class FSProxyLocal: | ||
| 37 | |||
| 38 | def __init__(self): | ||
| 39 | self._dirstack = [] | ||
| 40 | self._ignore = ['*.pyc'] + self._readignore() | ||
| 41 | |||
| 42 | def _close(self): | ||
| 43 | while self._dirstack: | ||
| 44 | self.back() | ||
| 45 | |||
| 46 | def _readignore(self): | ||
| 47 | file = self._hide('ignore') | ||
| 48 | try: | ||
| 49 | f = open(file) | ||
| 50 | except IOError: | ||
| 51 | file = self._hide('synctree.ignorefiles') | ||
| 52 | try: | ||
| 53 | f = open(file) | ||
| 54 | except IOError: | ||
| 55 | return [] | ||
| 56 | ignore = [] | ||
| 57 | while 1: | ||
| 58 | line = f.readline() | ||
| 59 | if not line: break | ||
| 60 | if line[-1] == '\n': line = line[:-1] | ||
| 61 | ignore.append(line) | ||
| 62 | f.close() | ||
| 63 | return ignore | ||
| 64 | |||
| 65 | def _hidden(self, name): | ||
| 66 | if os.name == 'mac': | ||
| 67 | return name[0] == '(' and name[-1] == ')' | ||
| 68 | else: | ||
| 69 | return name[0] == '.' | ||
| 70 | |||
| 71 | def _hide(self, name): | ||
| 72 | if os.name == 'mac': | ||
| 73 | return '(%s)' % name | ||
| 74 | else: | ||
| 75 | return '.%s' % name | ||
| 76 | |||
| 77 | def visible(self, name): | ||
| 78 | if len(name) > maxnamelen: return 0 | ||
| 79 | if name[-1] == '~': return 0 | ||
| 80 | if name in skipnames: return 0 | ||
| 81 | if self._hidden(name): return 0 | ||
| 82 | head, tail = os.path.split(name) | ||
| 83 | if head or not tail: return 0 | ||
| 84 | if macfs: | ||
| 85 | if os.path.exists(name) and not os.path.isdir(name): | ||
| 86 | try: | ||
| 87 | fs = macfs.FSSpec(name) | ||
| 88 | c, t = fs.GetCreatorType() | ||
| 89 | if t != 'TEXT': return 0 | ||
| 90 | except macfs.error, msg: | ||
| 91 | print "***", name, msg | ||
| 92 | return 0 | ||
| 93 | else: | ||
| 94 | if os.path.islink(name): return 0 | ||
| 95 | if '\0' in open(name, 'rb').read(512): return 0 | ||
| 96 | for ign in self._ignore: | ||
| 97 | if fnmatch.fnmatch(name, ign): return 0 | ||
| 98 | return 1 | ||
| 99 | |||
| 100 | def check(self, name): | ||
| 101 | if not self.visible(name): | ||
| 102 | raise os.error, "protected name %s" % repr(name) | ||
| 103 | |||
| 104 | def checkfile(self, name): | ||
| 105 | self.check(name) | ||
| 106 | if not os.path.isfile(name): | ||
| 107 | raise os.error, "not a plain file %s" % repr(name) | ||
| 108 | |||
| 109 | def pwd(self): | ||
| 110 | return os.getcwd() | ||
| 111 | |||
| 112 | def cd(self, name): | ||
| 113 | self.check(name) | ||
| 114 | save = os.getcwd(), self._ignore | ||
| 115 | os.chdir(name) | ||
| 116 | self._dirstack.append(save) | ||
| 117 | self._ignore = self._ignore + self._readignore() | ||
| 118 | |||
| 119 | def back(self): | ||
| 120 | if not self._dirstack: | ||
| 121 | raise os.error, "empty directory stack" | ||
| 122 | dir, ignore = self._dirstack[-1] | ||
| 123 | os.chdir(dir) | ||
| 124 | del self._dirstack[-1] | ||
| 125 | self._ignore = ignore | ||
| 126 | |||
| 127 | def _filter(self, files, pat = None): | ||
| 128 | if pat: | ||
| 129 | def keep(name, pat = pat): | ||
| 130 | return fnmatch.fnmatch(name, pat) | ||
| 131 | files = filter(keep, files) | ||
| 132 | files = filter(self.visible, files) | ||
| 133 | files.sort() | ||
| 134 | return files | ||
| 135 | |||
| 136 | def list(self, pat = None): | ||
| 137 | files = os.listdir(os.curdir) | ||
| 138 | return self._filter(files, pat) | ||
| 139 | |||
| 140 | def listfiles(self, pat = None): | ||
| 141 | files = os.listdir(os.curdir) | ||
| 142 | files = filter(os.path.isfile, files) | ||
| 143 | return self._filter(files, pat) | ||
| 144 | |||
| 145 | def listsubdirs(self, pat = None): | ||
| 146 | files = os.listdir(os.curdir) | ||
| 147 | files = filter(os.path.isdir, files) | ||
| 148 | return self._filter(files, pat) | ||
| 149 | |||
| 150 | def exists(self, name): | ||
| 151 | return self.visible(name) and os.path.exists(name) | ||
| 152 | |||
| 153 | def isdir(self, name): | ||
| 154 | return self.visible(name) and os.path.isdir(name) | ||
| 155 | |||
| 156 | def islink(self, name): | ||
| 157 | return self.visible(name) and os.path.islink(name) | ||
| 158 | |||
| 159 | def isfile(self, name): | ||
| 160 | return self.visible(name) and os.path.isfile(name) | ||
| 161 | |||
| 162 | def sum(self, name): | ||
| 163 | self.checkfile(name) | ||
| 164 | BUFFERSIZE = 1024*8 | ||
| 165 | f = open(name) | ||
| 166 | sum = md5.new() | ||
| 167 | while 1: | ||
| 168 | buffer = f.read(BUFFERSIZE) | ||
| 169 | if not buffer: | ||
| 170 | break | ||
| 171 | sum.update(buffer) | ||
| 172 | return sum.digest() | ||
| 173 | |||
| 174 | def size(self, name): | ||
| 175 | self.checkfile(name) | ||
| 176 | return os.stat(name)[ST_SIZE] | ||
| 177 | |||
| 178 | def mtime(self, name): | ||
| 179 | self.checkfile(name) | ||
| 180 | return time.localtime(os.stat(name)[ST_MTIME]) | ||
| 181 | |||
| 182 | def stat(self, name): | ||
| 183 | self.checkfile(name) | ||
| 184 | size = os.stat(name)[ST_SIZE] | ||
| 185 | mtime = time.localtime(os.stat(name)[ST_MTIME]) | ||
| 186 | return size, mtime | ||
| 187 | |||
| 188 | def info(self, name): | ||
| 189 | sum = self.sum(name) | ||
| 190 | size = os.stat(name)[ST_SIZE] | ||
| 191 | mtime = time.localtime(os.stat(name)[ST_MTIME]) | ||
| 192 | return sum, size, mtime | ||
| 193 | |||
| 194 | def _list(self, function, list): | ||
| 195 | if list is None: | ||
| 196 | list = self.listfiles() | ||
| 197 | res = [] | ||
| 198 | for name in list: | ||
| 199 | try: | ||
| 200 | res.append((name, function(name))) | ||
| 201 | except (os.error, IOError): | ||
| 202 | res.append((name, None)) | ||
| 203 | return res | ||
| 204 | |||
| 205 | def sumlist(self, list = None): | ||
| 206 | return self._list(self.sum, list) | ||
| 207 | |||
| 208 | def statlist(self, list = None): | ||
| 209 | return self._list(self.stat, list) | ||
| 210 | |||
| 211 | def mtimelist(self, list = None): | ||
| 212 | return self._list(self.mtime, list) | ||
| 213 | |||
| 214 | def sizelist(self, list = None): | ||
| 215 | return self._list(self.size, list) | ||
| 216 | |||
| 217 | def infolist(self, list = None): | ||
| 218 | return self._list(self.info, list) | ||
| 219 | |||
| 220 | def _dict(self, function, list): | ||
| 221 | if list is None: | ||
| 222 | list = self.listfiles() | ||
| 223 | dict = {} | ||
| 224 | for name in list: | ||
| 225 | try: | ||
| 226 | dict[name] = function(name) | ||
| 227 | except (os.error, IOError): | ||
| 228 | pass | ||
| 229 | return dict | ||
| 230 | |||
| 231 | def sumdict(self, list = None): | ||
| 232 | return self.dict(self.sum, list) | ||
| 233 | |||
| 234 | def sizedict(self, list = None): | ||
| 235 | return self.dict(self.size, list) | ||
| 236 | |||
| 237 | def mtimedict(self, list = None): | ||
| 238 | return self.dict(self.mtime, list) | ||
| 239 | |||
| 240 | def statdict(self, list = None): | ||
| 241 | return self.dict(self.stat, list) | ||
| 242 | |||
| 243 | def infodict(self, list = None): | ||
| 244 | return self._dict(self.info, list) | ||
| 245 | |||
| 246 | def read(self, name, offset = 0, length = -1): | ||
| 247 | self.checkfile(name) | ||
| 248 | f = open(name) | ||
| 249 | f.seek(offset) | ||
| 250 | if length == 0: | ||
| 251 | data = '' | ||
| 252 | elif length < 0: | ||
| 253 | data = f.read() | ||
| 254 | else: | ||
| 255 | data = f.read(length) | ||
| 256 | f.close() | ||
| 257 | return data | ||
| 258 | |||
| 259 | def create(self, name): | ||
| 260 | self.check(name) | ||
| 261 | if os.path.exists(name): | ||
| 262 | self.checkfile(name) | ||
| 263 | bname = name + '~' | ||
| 264 | try: | ||
| 265 | os.unlink(bname) | ||
| 266 | except os.error: | ||
| 267 | pass | ||
| 268 | os.rename(name, bname) | ||
| 269 | f = open(name, 'w') | ||
| 270 | f.close() | ||
| 271 | |||
| 272 | def write(self, name, data, offset = 0): | ||
| 273 | self.checkfile(name) | ||
| 274 | f = open(name, 'r+') | ||
| 275 | f.seek(offset) | ||
| 276 | f.write(data) | ||
| 277 | f.close() | ||
| 278 | |||
| 279 | def mkdir(self, name): | ||
| 280 | self.check(name) | ||
| 281 | os.mkdir(name, 0777) | ||
| 282 | |||
| 283 | def rmdir(self, name): | ||
| 284 | self.check(name) | ||
| 285 | os.rmdir(name) | ||
| 286 | |||
| 287 | |||
| 288 | class FSProxyServer(FSProxyLocal, server.Server): | ||
| 289 | |||
| 290 | def __init__(self, address, verbose = server.VERBOSE): | ||
| 291 | FSProxyLocal.__init__(self) | ||
| 292 | server.Server.__init__(self, address, verbose) | ||
| 293 | |||
| 294 | def _close(self): | ||
| 295 | server.Server._close(self) | ||
| 296 | FSProxyLocal._close(self) | ||
| 297 | |||
| 298 | def _serve(self): | ||
| 299 | server.Server._serve(self) | ||
| 300 | # Retreat into start directory | ||
| 301 | while self._dirstack: self.back() | ||
| 302 | |||
| 303 | |||
| 304 | class FSProxyClient(client.Client): | ||
| 305 | |||
| 306 | def __init__(self, address, verbose = client.VERBOSE): | ||
| 307 | client.Client.__init__(self, address, verbose) | ||
| 308 | |||
| 309 | |||
| 310 | def test(): | ||
| 311 | import string | ||
| 312 | import sys | ||
| 313 | if sys.argv[1:]: | ||
| 314 | port = string.atoi(sys.argv[1]) | ||
| 315 | else: | ||
| 316 | port = 4127 | ||
| 317 | proxy = FSProxyServer(('', port)) | ||
| 318 | proxy._serverloop() | ||
| 319 | |||
| 320 | |||
| 321 | if __name__ == '__main__': | ||
| 322 | test() | ||
| @@ -0,0 +1,198 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """RCS Proxy. | ||
| 4 | |||
| 5 | Provide a simplified interface on RCS files, locally or remotely. | ||
| 6 | The functionality is geared towards implementing some sort of | ||
| 7 | remote CVS like utility. It is modeled after the similar module | ||
| 8 | FSProxy. | ||
| 9 | |||
| 10 | The module defines two classes: | ||
| 11 | |||
| 12 | RCSProxyLocal -- used for local access | ||
| 13 | RCSProxyServer -- used on the server side of remote access | ||
| 14 | |||
| 15 | The corresponding client class, RCSProxyClient, is defined in module | ||
| 16 | rcsclient. | ||
| 17 | |||
| 18 | The remote classes are instantiated with an IP address and an optional | ||
| 19 | verbosity flag. | ||
| 20 | """ | ||
| 21 | |||
| 22 | import server | ||
| 23 | import md5 | ||
| 24 | import os | ||
| 25 | import fnmatch | ||
| 26 | import string | ||
| 27 | import tempfile | ||
| 28 | import rcslib | ||
| 29 | |||
| 30 | |||
| 31 | class DirSupport: | ||
| 32 | |||
| 33 | def __init__(self): | ||
| 34 | self._dirstack = [] | ||
| 35 | |||
| 36 | def __del__(self): | ||
| 37 | self._close() | ||
| 38 | |||
| 39 | def _close(self): | ||
| 40 | while self._dirstack: | ||
| 41 | self.back() | ||
| 42 | |||
| 43 | def pwd(self): | ||
| 44 | return os.getcwd() | ||
| 45 | |||
| 46 | def cd(self, name): | ||
| 47 | save = os.getcwd() | ||
| 48 | os.chdir(name) | ||
| 49 | self._dirstack.append(save) | ||
| 50 | |||
| 51 | def back(self): | ||
| 52 | if not self._dirstack: | ||
| 53 | raise os.error, "empty directory stack" | ||
| 54 | dir = self._dirstack[-1] | ||
| 55 | os.chdir(dir) | ||
| 56 | del self._dirstack[-1] | ||
| 57 | |||
| 58 | def listsubdirs(self, pat = None): | ||
| 59 | files = os.listdir(os.curdir) | ||
| 60 | files = filter(os.path.isdir, files) | ||
| 61 | return self._filter(files, pat) | ||
| 62 | |||
| 63 | def isdir(self, name): | ||
| 64 | return os.path.isdir(name) | ||
| 65 | |||
| 66 | def mkdir(self, name): | ||
| 67 | os.mkdir(name, 0777) | ||
| 68 | |||
| 69 | def rmdir(self, name): | ||
| 70 | os.rmdir(name) | ||
| 71 | |||
| 72 | |||
| 73 | class RCSProxyLocal(rcslib.RCS, DirSupport): | ||
| 74 | |||
| 75 | def __init__(self): | ||
| 76 | rcslib.RCS.__init__(self) | ||
| 77 | DirSupport.__init__(self) | ||
| 78 | |||
| 79 | def __del__(self): | ||
| 80 | DirSupport.__del__(self) | ||
| 81 | rcslib.RCS.__del__(self) | ||
| 82 | |||
| 83 | def sumlist(self, list = None): | ||
| 84 | return self._list(self.sum, list) | ||
| 85 | |||
| 86 | def sumdict(self, list = None): | ||
| 87 | return self._dict(self.sum, list) | ||
| 88 | |||
| 89 | def sum(self, name_rev): | ||
| 90 | f = self._open(name_rev) | ||
| 91 | BUFFERSIZE = 1024*8 | ||
| 92 | sum = md5.new() | ||
| 93 | while 1: | ||
| 94 | buffer = f.read(BUFFERSIZE) | ||
| 95 | if not buffer: | ||
| 96 | break | ||
| 97 | sum.update(buffer) | ||
| 98 | self._closepipe(f) | ||
| 99 | return sum.digest() | ||
| 100 | |||
| 101 | def get(self, name_rev): | ||
| 102 | f = self._open(name_rev) | ||
| 103 | data = f.read() | ||
| 104 | self._closepipe(f) | ||
| 105 | return data | ||
| 106 | |||
| 107 | def put(self, name_rev, data, message=None): | ||
| 108 | name, rev = self._unmangle(name_rev) | ||
| 109 | f = open(name, 'w') | ||
| 110 | f.write(data) | ||
| 111 | f.close() | ||
| 112 | self.checkin(name_rev, message) | ||
| 113 | self._remove(name) | ||
| 114 | |||
| 115 | def _list(self, function, list = None): | ||
| 116 | """INTERNAL: apply FUNCTION to all files in LIST. | ||
| 117 | |||
| 118 | Return a list of the results. | ||
| 119 | |||
| 120 | The list defaults to all files in the directory if None. | ||
| 121 | |||
| 122 | """ | ||
| 123 | if list is None: | ||
| 124 | list = self.listfiles() | ||
| 125 | res = [] | ||
| 126 | for name in list: | ||
| 127 | try: | ||
| 128 | res.append((name, function(name))) | ||
| 129 | except (os.error, IOError): | ||
| 130 | res.append((name, None)) | ||
| 131 | return res | ||
| 132 | |||
| 133 | def _dict(self, function, list = None): | ||
| 134 | """INTERNAL: apply FUNCTION to all files in LIST. | ||
| 135 | |||
| 136 | Return a dictionary mapping files to results. | ||
| 137 | |||
| 138 | The list defaults to all files in the directory if None. | ||
| 139 | |||
| 140 | """ | ||
| 141 | if list is None: | ||
| 142 | list = self.listfiles() | ||
| 143 | dict = {} | ||
| 144 | for name in list: | ||
| 145 | try: | ||
| 146 | dict[name] = function(name) | ||
| 147 | except (os.error, IOError): | ||
| 148 | pass | ||
| 149 | return dict | ||
| 150 | |||
| 151 | |||
| 152 | class RCSProxyServer(RCSProxyLocal, server.SecureServer): | ||
| 153 | |||
| 154 | def __init__(self, address, verbose = server.VERBOSE): | ||
| 155 | RCSProxyLocal.__init__(self) | ||
| 156 | server.SecureServer.__init__(self, address, verbose) | ||
| 157 | |||
| 158 | def _close(self): | ||
| 159 | server.SecureServer._close(self) | ||
| 160 | RCSProxyLocal._close(self) | ||
| 161 | |||
| 162 | def _serve(self): | ||
| 163 | server.SecureServer._serve(self) | ||
| 164 | # Retreat into start directory | ||
| 165 | while self._dirstack: self.back() | ||
| 166 | |||
| 167 | |||
| 168 | def test_server(): | ||
| 169 | import string | ||
| 170 | import sys | ||
| 171 | if sys.argv[1:]: | ||
| 172 | port = string.atoi(sys.argv[1]) | ||
| 173 | else: | ||
| 174 | port = 4127 | ||
| 175 | proxy = RCSProxyServer(('', port)) | ||
| 176 | proxy._serverloop() | ||
| 177 | |||
| 178 | |||
| 179 | def test(): | ||
| 180 | import sys | ||
| 181 | if not sys.argv[1:] or sys.argv[1] and sys.argv[1][0] in '0123456789': | ||
| 182 | test_server() | ||
| 183 | sys.exit(0) | ||
| 184 | proxy = RCSProxyLocal() | ||
| 185 | what = sys.argv[1] | ||
| 186 | if hasattr(proxy, what): | ||
| 187 | attr = getattr(proxy, what) | ||
| 188 | if callable(attr): | ||
| 189 | print apply(attr, tuple(sys.argv[2:])) | ||
| 190 | else: | ||
| 191 | print repr(attr) | ||
| 192 | else: | ||
| 193 | print "%s: no such attribute" % what | ||
| 194 | sys.exit(2) | ||
| 195 | |||
| 196 | |||
| 197 | if __name__ == '__main__': | ||
| 198 | test() | ||
| @@ -0,0 +1,121 @@ | |||
| 1 | Filesystem, RCS and CVS client and server classes | ||
| 2 | ================================================= | ||
| 3 | |||
| 4 | *** See the security warning at the end of this file! *** | ||
| 5 | |||
| 6 | This directory contains various modules and classes that support | ||
| 7 | remote file system operations. | ||
| 8 | |||
| 9 | CVS stuff | ||
| 10 | --------- | ||
| 11 | |||
| 12 | rcvs Script to put in your bin directory | ||
| 13 | rcvs.py Remote CVS client command line interface | ||
| 14 | |||
| 15 | cvslib.py CVS admin files classes (used by rrcs) | ||
| 16 | cvslock.py CVS locking algorithms | ||
| 17 | |||
| 18 | RCS stuff | ||
| 19 | --------- | ||
| 20 | |||
| 21 | rrcs Script to put in your bin directory | ||
| 22 | rrcs.py Remote RCS client command line interface | ||
| 23 | |||
| 24 | rcsclient.py Return an RCSProxyClient instance | ||
| 25 | (has reasonable default server/port/directory) | ||
| 26 | |||
| 27 | RCSProxy.py RCS proxy and server classes (on top of rcslib.py) | ||
| 28 | |||
| 29 | rcslib.py Local-only RCS base class (affects stdout & | ||
| 30 | local work files) | ||
| 31 | |||
| 32 | FSProxy stuff | ||
| 33 | ------------- | ||
| 34 | |||
| 35 | sumtree.py Old demo for FSProxy | ||
| 36 | cmptree.py First FSProxy client (used to sync from the Mac) | ||
| 37 | FSProxy.py Filesystem interface classes | ||
| 38 | |||
| 39 | Generic client/server stuff | ||
| 40 | --------------------------- | ||
| 41 | |||
| 42 | client.py Client class | ||
| 43 | server.py Server class | ||
| 44 | |||
| 45 | security.py Security mix-in class (not very secure I think) | ||
| 46 | |||
| 47 | Other generic stuff | ||
| 48 | ------------------- | ||
| 49 | |||
| 50 | cmdfw.py CommandFrameWork class | ||
| 51 | (used by rcvs, should be used by rrcs as well) | ||
| 52 | |||
| 53 | |||
| 54 | Client/Server operation | ||
| 55 | ----------------------- | ||
| 56 | |||
| 57 | The Client and Server classes implement a simple-minded RPC protocol, | ||
| 58 | using Python's pickle module to transfer arguments, return values and | ||
| 59 | exceptions with the most generality. The Server class is instantiated | ||
| 60 | with a port number on which it should listen for requests; the Client | ||
| 61 | class is instantiated with a host name and a port number where it | ||
| 62 | should connect to. Once a client is connected, a TCP connection is | ||
| 63 | maintained between client and server. | ||
| 64 | |||
| 65 | The Server class currently handles only one connection at a time; | ||
| 66 | however it could be rewritten to allow various modes of operations, | ||
| 67 | using multiple threads or processes or the select() system call as | ||
| 68 | desired to serve multiple clients simultaneously (when using select(), | ||
| 69 | still handling one request at a time). This would not require | ||
| 70 | rewriting of the Client class. It may also be possible to adapt the | ||
| 71 | code to use UDP instead of TCP, but then both classes will have to be | ||
| 72 | rewritten (and unless extensive acknowlegements and request serial | ||
| 73 | numbers are used, the server should handle duplicate requests, so its | ||
| 74 | semantics should be idempotent -- shrudder). | ||
| 75 | |||
| 76 | Even though the FSProxy and RCSProxy modules define client classes, | ||
| 77 | the client class is fully generic -- what methods it supports is | ||
| 78 | determined entirely by the server. The server class, however, must be | ||
| 79 | derived from. This is generally done as follows: | ||
| 80 | |||
| 81 | from server import Server | ||
| 82 | from client import Client | ||
| 83 | |||
| 84 | # Define a class that performs the operations locally | ||
| 85 | class MyClassLocal: | ||
| 86 | def __init__(self): ... | ||
| 87 | def _close(self): ... | ||
| 88 | |||
| 89 | # Derive a server class using multiple inheritance | ||
| 90 | class MyClassServer(MyClassLocal, Server): | ||
| 91 | def __init__(self, address): | ||
| 92 | # Must initialize MyClassLocal as well as Server | ||
| 93 | MyClassLocal.__init__(self) | ||
| 94 | Server.__init__(self, address) | ||
| 95 | def _close(self): | ||
| 96 | Server._close() | ||
| 97 | MyClassLocal._close() | ||
| 98 | |||
| 99 | # A dummy client class | ||
| 100 | class MyClassClient(Client): pass | ||
| 101 | |||
| 102 | Note that because MyClassLocal isn't used in the definition of | ||
| 103 | MyClassClient, it would actually be better to place it in a separate | ||
| 104 | module so the definition of MyClassLocal isn't executed when we only | ||
| 105 | instantiate a client. | ||
| 106 | |||
| 107 | The modules client and server should probably be renamed to Client and | ||
| 108 | Server in order to match the class names. | ||
| 109 | |||
| 110 | |||
| 111 | *** Security warning: this version requires that you have a file | ||
| 112 | $HOME/.python_keyfile at the server and client side containing two | ||
| 113 | comma- separated numbers. The security system at the moment makes no | ||
| 114 | guarantees of actuallng being secure -- however it requires that the | ||
| 115 | key file exists and contains the same numbers at both ends for this to | ||
| 116 | work. (You can specify an alternative keyfile in $PYTHON_KEYFILE). | ||
| 117 | Have a look at the Security class in security.py for details; | ||
| 118 | basically, if the key file contains (x, y), then the security server | ||
| 119 | class chooses a random number z (the challenge) in the range | ||
| 120 | 10..100000 and the client must be able to produce pow(z, x, y) | ||
| 121 | (i.e. z**x mod y). | ||
| @@ -0,0 +1,157 @@ | |||
| 1 | """RPC Client module.""" | ||
| 2 | |||
| 3 | import sys | ||
| 4 | import socket | ||
| 5 | import pickle | ||
| 6 | import __builtin__ | ||
| 7 | import os | ||
| 8 | |||
| 9 | |||
| 10 | # Default verbosity (0 = silent, 1 = print connections, 2 = print requests too) | ||
| 11 | VERBOSE = 1 | ||
| 12 | |||
| 13 | |||
| 14 | class Client: | ||
| 15 | |||
| 16 | """RPC Client class. No need to derive a class -- it's fully generic.""" | ||
| 17 | |||
| 18 | def __init__(self, address, verbose = VERBOSE): | ||
| 19 | self._pre_init(address, verbose) | ||
| 20 | self._post_init() | ||
| 21 | |||
| 22 | def _pre_init(self, address, verbose = VERBOSE): | ||
| 23 | if type(address) == type(0): | ||
| 24 | address = ('', address) | ||
| 25 | self._address = address | ||
| 26 | self._verbose = verbose | ||
| 27 | if self._verbose: print "Connecting to %s ..." % repr(address) | ||
| 28 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 29 | self._socket.connect(address) | ||
| 30 | if self._verbose: print "Connected." | ||
| 31 | self._lastid = 0 # Last id for which a reply has been received | ||
| 32 | self._nextid = 1 # Id of next request | ||
| 33 | self._replies = {} # Unprocessed replies | ||
| 34 | self._rf = self._socket.makefile('r') | ||
| 35 | self._wf = self._socket.makefile('w') | ||
| 36 | |||
| 37 | def _post_init(self): | ||
| 38 | self._methods = self._call('.methods') | ||
| 39 | |||
| 40 | def __del__(self): | ||
| 41 | self._close() | ||
| 42 | |||
| 43 | def _close(self): | ||
| 44 | if self._rf: self._rf.close() | ||
| 45 | self._rf = None | ||
| 46 | if self._wf: self._wf.close() | ||
| 47 | self._wf = None | ||
| 48 | if self._socket: self._socket.close() | ||
| 49 | self._socket = None | ||
| 50 | |||
| 51 | def __getattr__(self, name): | ||
| 52 | if name in self._methods: | ||
| 53 | method = _stub(self, name) | ||
| 54 | setattr(self, name, method) # XXX circular reference | ||
| 55 | return method | ||
| 56 | raise AttributeError, name | ||
| 57 | |||
| 58 | def _setverbose(self, verbose): | ||
| 59 | self._verbose = verbose | ||
| 60 | |||
| 61 | def _call(self, name, *args): | ||
| 62 | return self._vcall(name, args) | ||
| 63 | |||
| 64 | def _vcall(self, name, args): | ||
| 65 | return self._recv(self._vsend(name, args)) | ||
| 66 | |||
| 67 | def _send(self, name, *args): | ||
| 68 | return self._vsend(name, args) | ||
| 69 | |||
| 70 | def _send_noreply(self, name, *args): | ||
| 71 | return self._vsend(name, args, 0) | ||
| 72 | |||
| 73 | def _vsend_noreply(self, name, args): | ||
| 74 | return self._vsend(name, args, 0) | ||
| 75 | |||
| 76 | def _vsend(self, name, args, wantreply = 1): | ||
| 77 | id = self._nextid | ||
| 78 | self._nextid = id+1 | ||
| 79 | if not wantreply: id = -id | ||
| 80 | request = (name, args, id) | ||
| 81 | if self._verbose > 1: print "sending request: %s" % repr(request) | ||
| 82 | wp = pickle.Pickler(self._wf) | ||
| 83 | wp.dump(request) | ||
| 84 | return id | ||
| 85 | |||
| 86 | def _recv(self, id): | ||
| 87 | exception, value, rid = self._vrecv(id) | ||
| 88 | if rid != id: | ||
| 89 | raise RuntimeError, "request/reply id mismatch: %d/%d" % (id, rid) | ||
| 90 | if exception is None: | ||
| 91 | return value | ||
| 92 | x = exception | ||
| 93 | if hasattr(__builtin__, exception): | ||
| 94 | x = getattr(__builtin__, exception) | ||
| 95 | elif exception in ('posix.error', 'mac.error'): | ||
| 96 | x = os.error | ||
| 97 | if x == exception: | ||
| 98 | exception = x | ||
| 99 | raise exception, value | ||
| 100 | |||
| 101 | def _vrecv(self, id): | ||
| 102 | self._flush() | ||
| 103 | if self._replies.has_key(id): | ||
| 104 | if self._verbose > 1: print "retrieving previous reply, id = %d" % id | ||
| 105 | reply = self._replies[id] | ||
| 106 | del self._replies[id] | ||
| 107 | return reply | ||
| 108 | aid = abs(id) | ||
| 109 | while 1: | ||
| 110 | if self._verbose > 1: print "waiting for reply, id = %d" % id | ||
| 111 | rp = pickle.Unpickler(self._rf) | ||
| 112 | reply = rp.load() | ||
| 113 | del rp | ||
| 114 | if self._verbose > 1: print "got reply: %s" % repr(reply) | ||
| 115 | rid = reply[2] | ||
| 116 | arid = abs(rid) | ||
| 117 | if arid == aid: | ||
| 118 | if self._verbose > 1: print "got it" | ||
| 119 | return reply | ||
| 120 | self._replies[rid] = reply | ||
| 121 | if arid > aid: | ||
| 122 | if self._verbose > 1: print "got higher id, assume all ok" | ||
| 123 | return (None, None, id) | ||
| 124 | |||
| 125 | def _flush(self): | ||
| 126 | self._wf.flush() | ||
| 127 | |||
| 128 | |||
| 129 | from security import Security | ||
| 130 | |||
| 131 | |||
| 132 | class SecureClient(Client, Security): | ||
| 133 | |||
| 134 | def __init__(self, *args): | ||
| 135 | import string | ||
| 136 | apply(self._pre_init, args) | ||
| 137 | Security.__init__(self) | ||
| 138 | self._wf.flush() | ||
| 139 | line = self._rf.readline() | ||
| 140 | challenge = string.atoi(string.strip(line)) | ||
| 141 | response = self._encode_challenge(challenge) | ||
| 142 | line = repr(long(response)) | ||
| 143 | if line[-1] in 'Ll': line = line[:-1] | ||
| 144 | self._wf.write(line + '\n') | ||
| 145 | self._wf.flush() | ||
| 146 | self._post_init() | ||
| 147 | |||
| 148 | class _stub: | ||
| 149 | |||
| 150 | """Helper class for Client -- each instance serves as a method of the client.""" | ||
| 151 | |||
| 152 | def __init__(self, client, name): | ||
| 153 | self._client = client | ||
| 154 | self._name = name | ||
| 155 | |||
| 156 | def __call__(self, *args): | ||
| 157 | return self._client._vcall(self._name, args) | ||
| @@ -0,0 +1,144 @@ | |||
| 1 | "Framework for command line interfaces like CVS. See class CmdFrameWork." | ||
| 2 | |||
| 3 | |||
| 4 | class CommandFrameWork: | ||
| 5 | |||
| 6 | """Framework class for command line interfaces like CVS. | ||
| 7 | |||
| 8 | The general command line structure is | ||
| 9 | |||
| 10 | command [flags] subcommand [subflags] [argument] ... | ||
| 11 | |||
| 12 | There's a class variable GlobalFlags which specifies the | ||
| 13 | global flags options. Subcommands are defined by defining | ||
| 14 | methods named do_<subcommand>. Flags for the subcommand are | ||
| 15 | defined by defining class or instance variables named | ||
| 16 | flags_<subcommand>. If there's no command, method default() | ||
| 17 | is called. The __doc__ strings for the do_ methods are used | ||
| 18 | for the usage message, printed after the general usage message | ||
| 19 | which is the class variable UsageMessage. The class variable | ||
| 20 | PostUsageMessage is printed after all the do_ methods' __doc__ | ||
| 21 | strings. The method's return value can be a suggested exit | ||
| 22 | status. [XXX Need to rewrite this to clarify it.] | ||
| 23 | |||
| 24 | Common usage is to derive a class, instantiate it, and then call its | ||
| 25 | run() method; by default this takes its arguments from sys.argv[1:]. | ||
| 26 | """ | ||
| 27 | |||
| 28 | UsageMessage = \ | ||
| 29 | "usage: (name)s [flags] subcommand [subflags] [argument] ..." | ||
| 30 | |||
| 31 | PostUsageMessage = None | ||
| 32 | |||
| 33 | GlobalFlags = '' | ||
| 34 | |||
| 35 | def __init__(self): | ||
| 36 | """Constructor, present for completeness.""" | ||
| 37 | pass | ||
| 38 | |||
| 39 | def run(self, args = None): | ||
| 40 | """Process flags, subcommand and options, then run it.""" | ||
| 41 | import getopt, sys | ||
| 42 | if args is None: args = sys.argv[1:] | ||
| 43 | try: | ||
| 44 | opts, args = getopt.getopt(args, self.GlobalFlags) | ||
| 45 | except getopt.error, msg: | ||
| 46 | return self.usage(msg) | ||
| 47 | self.options(opts) | ||
| 48 | if not args: | ||
| 49 | self.ready() | ||
| 50 | return self.default() | ||
| 51 | else: | ||
| 52 | cmd = args[0] | ||
| 53 | mname = 'do_' + cmd | ||
| 54 | fname = 'flags_' + cmd | ||
| 55 | try: | ||
| 56 | method = getattr(self, mname) | ||
| 57 | except AttributeError: | ||
| 58 | return self.usage("command %r unknown" % (cmd,)) | ||
| 59 | try: | ||
| 60 | flags = getattr(self, fname) | ||
| 61 | except AttributeError: | ||
| 62 | flags = '' | ||
| 63 | try: | ||
| 64 | opts, args = getopt.getopt(args[1:], flags) | ||
| 65 | except getopt.error, msg: | ||
| 66 | return self.usage( | ||
| 67 | "subcommand %s: " % cmd + str(msg)) | ||
| 68 | self.ready() | ||
| 69 | return method(opts, args) | ||
| 70 | |||
| 71 | def options(self, opts): | ||
| 72 | """Process the options retrieved by getopt. | ||
| 73 | Override this if you have any options.""" | ||
| 74 | if opts: | ||
| 75 | print "-"*40 | ||
| 76 | print "Options:" | ||
| 77 | for o, a in opts: | ||
| 78 | print 'option', o, 'value', repr(a) | ||
| 79 | print "-"*40 | ||
| 80 | |||
| 81 | def ready(self): | ||
| 82 | """Called just before calling the subcommand.""" | ||
| 83 | pass | ||
| 84 | |||
| 85 | def usage(self, msg = None): | ||
| 86 | """Print usage message. Return suitable exit code (2).""" | ||
| 87 | if msg: print msg | ||
| 88 | print self.UsageMessage % {'name': self.__class__.__name__} | ||
| 89 | docstrings = {} | ||
| 90 | c = self.__class__ | ||
| 91 | while 1: | ||
| 92 | for name in dir(c): | ||
| 93 | if name[:3] == 'do_': | ||
| 94 | if docstrings.has_key(name): | ||
| 95 | continue | ||
| 96 | try: | ||
| 97 | doc = getattr(c, name).__doc__ | ||
| 98 | except: | ||
| 99 | doc = None | ||
| 100 | if doc: | ||
| 101 | docstrings[name] = doc | ||
| 102 | if not c.__bases__: | ||
| 103 | break | ||
| 104 | c = c.__bases__[0] | ||
| 105 | if docstrings: | ||
| 106 | print "where subcommand can be:" | ||
| 107 | names = docstrings.keys() | ||
| 108 | names.sort() | ||
| 109 | for name in names: | ||
| 110 | print docstrings[name] | ||
| 111 | if self.PostUsageMessage: | ||
| 112 | print self.PostUsageMessage | ||
| 113 | return 2 | ||
| 114 | |||
| 115 | def default(self): | ||
| 116 | """Default method, called when no subcommand is given. | ||
| 117 | You should always override this.""" | ||
| 118 | print "Nobody expects the Spanish Inquisition!" | ||
| 119 | |||
| 120 | |||
| 121 | def test(): | ||
| 122 | """Test script -- called when this module is run as a script.""" | ||
| 123 | import sys | ||
| 124 | class Hello(CommandFrameWork): | ||
| 125 | def do_hello(self, opts, args): | ||
| 126 | "hello -- print 'hello world', needs no arguments" | ||
| 127 | print "Hello, world" | ||
| 128 | x = Hello() | ||
| 129 | tests = [ | ||
| 130 | [], | ||
| 131 | ['hello'], | ||
| 132 | ['spam'], | ||
| 133 | ['-x'], | ||
| 134 | ['hello', '-x'], | ||
| 135 | None, | ||
| 136 | ] | ||
| 137 | for t in tests: | ||
| 138 | print '-'*10, t, '-'*10 | ||
| 139 | sts = x.run(t) | ||
| 140 | print "Exit status:", repr(sts) | ||
| 141 | |||
| 142 | |||
| 143 | if __name__ == '__main__': | ||
| 144 | test() | ||
| @@ -0,0 +1,208 @@ | |||
| 1 | """Compare local and remote dictionaries and transfer differing files -- like rdist.""" | ||
| 2 | |||
| 3 | import sys | ||
| 4 | from repr import repr | ||
| 5 | import FSProxy | ||
| 6 | import time | ||
| 7 | import os | ||
| 8 | |||
| 9 | def main(): | ||
| 10 | pwd = os.getcwd() | ||
| 11 | s = raw_input("chdir [%s] " % pwd) | ||
| 12 | if s: | ||
| 13 | os.chdir(s) | ||
| 14 | pwd = os.getcwd() | ||
| 15 | host = ask("host", 'voorn.cwi.nl') | ||
| 16 | port = 4127 | ||
| 17 | verbose = 1 | ||
| 18 | mode = '' | ||
| 19 | print """\ | ||
| 20 | Mode should be a string of characters, indicating what to do with differences. | ||
| 21 | r - read different files to local file system | ||
| 22 | w - write different files to remote file system | ||
| 23 | c - create new files, either remote or local | ||
| 24 | d - delete disappearing files, either remote or local | ||
| 25 | """ | ||
| 26 | s = raw_input("mode [%s] " % mode) | ||
| 27 | if s: mode = s | ||
| 28 | address = (host, port) | ||
| 29 | t1 = time.time() | ||
| 30 | local = FSProxy.FSProxyLocal() | ||
| 31 | remote = FSProxy.FSProxyClient(address, verbose) | ||
| 32 | compare(local, remote, mode) | ||
| 33 | remote._close() | ||
| 34 | local._close() | ||
| 35 | t2 = time.time() | ||
| 36 | dt = t2-t1 | ||
| 37 | mins, secs = divmod(dt, 60) | ||
| 38 | print mins, "minutes and", round(secs), "seconds" | ||
| 39 | raw_input("[Return to exit] ") | ||
| 40 | |||
| 41 | def ask(prompt, default): | ||
| 42 | s = raw_input("%s [%s] " % (prompt, default)) | ||
| 43 | return s or default | ||
| 44 | |||
| 45 | def askint(prompt, default): | ||
| 46 | s = raw_input("%s [%s] " % (prompt, str(default))) | ||
| 47 | if s: return string.atoi(s) | ||
| 48 | return default | ||
| 49 | |||
| 50 | def compare(local, remote, mode): | ||
| 51 | |||
| 52 | print "PWD =", repr(os.getcwd()) | ||
| 53 | sums_id = remote._send('sumlist') | ||
| 54 | subdirs_id = remote._send('listsubdirs') | ||
| 55 | remote._flush() | ||
| 56 | print "calculating local sums ..." | ||
| 57 | lsumdict = {} | ||
| 58 | for name, info in local.sumlist(): | ||
| 59 | lsumdict[name] = info | ||
| 60 | print "getting remote sums ..." | ||
| 61 | sums = remote._recv(sums_id) | ||
| 62 | print "got", len(sums) | ||
| 63 | rsumdict = {} | ||
| 64 | for name, rsum in sums: | ||
| 65 | rsumdict[name] = rsum | ||
| 66 | if not lsumdict.has_key(name): | ||
| 67 | print repr(name), "only remote" | ||
| 68 | if 'r' in mode and 'c' in mode: | ||
| 69 | recvfile(local, remote, name) | ||
| 70 | else: | ||
| 71 | lsum = lsumdict[name] | ||
| 72 | if lsum != rsum: | ||
| 73 | print repr(name), | ||
| 74 | rmtime = remote.mtime(name) | ||
| 75 | lmtime = local.mtime(name) | ||
| 76 | if rmtime > lmtime: | ||
| 77 | print "remote newer", | ||
| 78 | if 'r' in mode: | ||
| 79 | recvfile(local, remote, name) | ||
| 80 | elif lmtime > rmtime: | ||
| 81 | print "local newer", | ||
| 82 | if 'w' in mode: | ||
| 83 | sendfile(local, remote, name) | ||
| 84 | else: | ||
| 85 | print "same mtime but different sum?!?!", | ||
| 86 | |||
| 87 | for name in lsumdict.keys(): | ||
| 88 | if not rsumdict.keys(): | ||
| 89 | print repr(name), "only locally", | ||
| 90 | fl() | ||
| 91 | if 'w' in mode and 'c' in mode: | ||
| 92 | sendfile(local, remote, name) | ||
| 93 | elif 'r' in mode and 'd' in mode: | ||
| 94 | os.unlink(name) | ||
| 95 | print "removed." | ||
| 96 | |||
| 97 | print "gettin subdirs ..." | ||
| 98 | subdirs = remote._recv(subdirs_id) | ||
| 99 | common = [] | ||
| 100 | for name in subdirs: | ||
| 101 | if local.isdir(name): | ||
| 102 | print "Common subdirectory", repr(name) | ||
| 103 | common.append(name) | ||
| 104 | else: | ||
| 105 | print "Remote subdirectory", repr(name), "not found locally" | ||
| 106 | if 'r' in mode and 'c' in mode: | ||
| 107 | pr = "Create local subdirectory %s? [y] " % \ | ||
| 108 | repr(name) | ||
| 109 | if 'y' in mode: | ||
| 110 | ok = 'y' | ||
| 111 | else: | ||
| 112 | ok = ask(pr, "y") | ||
| 113 | if ok[:1] in ('y', 'Y'): | ||
| 114 | local.mkdir(name) | ||
| 115 | print "Subdirectory %s made" % \ | ||
| 116 | repr(name) | ||
| 117 | common.append(name) | ||
| 118 | lsubdirs = local.listsubdirs() | ||
| 119 | for name in lsubdirs: | ||
| 120 | if name not in subdirs: | ||
| 121 | print "Local subdirectory", repr(name), "not found remotely" | ||
| 122 | for name in common: | ||
| 123 | print "Entering subdirectory", repr(name) | ||
| 124 | local.cd(name) | ||
| 125 | remote.cd(name) | ||
| 126 | compare(local, remote, mode) | ||
| 127 | remote.back() | ||
| 128 | local.back() | ||
| 129 | |||
| 130 | def sendfile(local, remote, name): | ||
| 131 | try: | ||
| 132 | remote.create(name) | ||
| 133 | except (IOError, os.error), msg: | ||
| 134 | print "cannot create:", msg | ||
| 135 | return | ||
| 136 | |||
| 137 | print "sending ...", | ||
| 138 | fl() | ||
| 139 | |||
| 140 | data = open(name).read() | ||
| 141 | |||
| 142 | t1 = time.time() | ||
| 143 | |||
| 144 | remote._send_noreply('write', name, data) | ||
| 145 | remote._flush() | ||
| 146 | |||
| 147 | t2 = time.time() | ||
| 148 | |||
| 149 | dt = t2-t1 | ||
| 150 | print len(data), "bytes in", round(dt), "seconds", | ||
| 151 | if dt: | ||
| 152 | print "i.e.", round(len(data)/dt), "bytes/sec", | ||
| 153 | |||
| 154 | |||
| 155 | def recvfile(local, remote, name): | ||
| 156 | ok = 0 | ||
| 157 | try: | ||
| 158 | rv = recvfile_real(local, remote, name) | ||
| 159 | ok = 1 | ||
| 160 | return rv | ||
| 161 | finally: | ||
| 162 | if not ok: | ||
| 163 | print "*** recvfile of %r failed, deleting" % (name,) | ||
| 164 | local.delete(name) | ||
| 165 | |||
| 166 | def recvfile_real(local, remote, name): | ||
| 167 | try: | ||
| 168 | local.create(name) | ||
| 169 | except (IOError, os.error), msg: | ||
| 170 | print "cannot create:", msg | ||
| 171 | return | ||
| 172 | |||
| 173 | print "receiving ...", | ||
| 174 | fl() | ||
| 175 | |||
| 176 | f = open(name, 'w') | ||
| 177 | t1 = time.time() | ||
| 178 | |||
| 179 | length = 4*1024 | ||
| 180 | offset = 0 | ||
| 181 | id = remote._send('read', name, offset, length) | ||
| 182 | remote._flush() | ||
| 183 | while 1: | ||
| 184 | newoffset = offset + length | ||
| 185 | newid = remote._send('read', name, newoffset, length) | ||
| 186 | data = remote._recv(id) | ||
| 187 | id = newid | ||
| 188 | if not data: break | ||
| 189 | f.seek(offset) | ||
| 190 | f.write(data) | ||
| 191 | offset = newoffset | ||
| 192 | size = f.tell() | ||
| 193 | |||
| 194 | t2 = time.time() | ||
| 195 | f.close() | ||
| 196 | |||
| 197 | dt = t2-t1 | ||
| 198 | print size, "bytes in", round(dt), "seconds", | ||
| 199 | if dt: | ||
| 200 | print "i.e.", int(size/dt), "bytes/sec", | ||
| 201 | |||
| 202 | remote._recv(id) # ignored | ||
| 203 | |||
| 204 | def fl(): | ||
| 205 | sys.stdout.flush() | ||
| 206 | |||
| 207 | if __name__ == '__main__': | ||
| 208 | main() | ||
| @@ -0,0 +1,364 @@ | |||
| 1 | """Utilities for CVS administration.""" | ||
| 2 | |||
| 3 | import string | ||
| 4 | import os | ||
| 5 | import time | ||
| 6 | import md5 | ||
| 7 | import fnmatch | ||
| 8 | |||
| 9 | if not hasattr(time, 'timezone'): | ||
| 10 | time.timezone = 0 | ||
| 11 | |||
| 12 | class File: | ||
| 13 | |||
| 14 | """Represent a file's status. | ||
| 15 | |||
| 16 | Instance variables: | ||
| 17 | |||
| 18 | file -- the filename (no slashes), None if uninitialized | ||
| 19 | lseen -- true if the data for the local file is up to date | ||
| 20 | eseen -- true if the data from the CVS/Entries entry is up to date | ||
| 21 | (this implies that the entry must be written back) | ||
| 22 | rseen -- true if the data for the remote file is up to date | ||
| 23 | proxy -- RCSProxy instance used to contact the server, or None | ||
| 24 | |||
| 25 | Note that lseen and rseen don't necessary mean that a local | ||
| 26 | or remote file *exists* -- they indicate that we've checked it. | ||
| 27 | However, eseen means that this instance corresponds to an | ||
| 28 | entry in the CVS/Entries file. | ||
| 29 | |||
| 30 | If lseen is true: | ||
| 31 | |||
| 32 | lsum -- checksum of the local file, None if no local file | ||
| 33 | lctime -- ctime of the local file, None if no local file | ||
| 34 | lmtime -- mtime of the local file, None if no local file | ||
| 35 | |||
| 36 | If eseen is true: | ||
| 37 | |||
| 38 | erev -- revision, None if this is a no revision (not '0') | ||
| 39 | enew -- true if this is an uncommitted added file | ||
| 40 | edeleted -- true if this is an uncommitted removed file | ||
| 41 | ectime -- ctime of last local file corresponding to erev | ||
| 42 | emtime -- mtime of last local file corresponding to erev | ||
| 43 | extra -- 5th string from CVS/Entries file | ||
| 44 | |||
| 45 | If rseen is true: | ||
| 46 | |||
| 47 | rrev -- revision of head, None if non-existent | ||
| 48 | rsum -- checksum of that revision, Non if non-existent | ||
| 49 | |||
| 50 | If eseen and rseen are both true: | ||
| 51 | |||
| 52 | esum -- checksum of revision erev, None if no revision | ||
| 53 | |||
| 54 | Note | ||
| 55 | """ | ||
| 56 | |||
| 57 | def __init__(self, file = None): | ||
| 58 | if file and '/' in file: | ||
| 59 | raise ValueError, "no slash allowed in file" | ||
| 60 | self.file = file | ||
| 61 | self.lseen = self.eseen = self.rseen = 0 | ||
| 62 | self.proxy = None | ||
| 63 | |||
| 64 | def __cmp__(self, other): | ||
| 65 | return cmp(self.file, other.file) | ||
| 66 | |||
| 67 | def getlocal(self): | ||
| 68 | try: | ||
| 69 | self.lmtime, self.lctime = os.stat(self.file)[-2:] | ||
| 70 | except os.error: | ||
| 71 | self.lmtime = self.lctime = self.lsum = None | ||
| 72 | else: | ||
| 73 | self.lsum = md5.new(open(self.file).read()).digest() | ||
| 74 | self.lseen = 1 | ||
| 75 | |||
| 76 | def getentry(self, line): | ||
| 77 | words = string.splitfields(line, '/') | ||
| 78 | if self.file and words[1] != self.file: | ||
| 79 | raise ValueError, "file name mismatch" | ||
| 80 | self.file = words[1] | ||
| 81 | self.erev = words[2] | ||
| 82 | self.edeleted = 0 | ||
| 83 | self.enew = 0 | ||
| 84 | self.ectime = self.emtime = None | ||
| 85 | if self.erev[:1] == '-': | ||
| 86 | self.edeleted = 1 | ||
| 87 | self.erev = self.erev[1:] | ||
| 88 | if self.erev == '0': | ||
| 89 | self.erev = None | ||
| 90 | self.enew = 1 | ||
| 91 | else: | ||
| 92 | dates = words[3] | ||
| 93 | self.ectime = unctime(dates[:24]) | ||
| 94 | self.emtime = unctime(dates[25:]) | ||
| 95 | self.extra = words[4] | ||
| 96 | if self.rseen: | ||
| 97 | self.getesum() | ||
| 98 | self.eseen = 1 | ||
| 99 | |||
| 100 | def getremote(self, proxy = None): | ||
| 101 | if proxy: | ||
| 102 | self.proxy = proxy | ||
| 103 | try: | ||
| 104 | self.rrev = self.proxy.head(self.file) | ||
| 105 | except (os.error, IOError): | ||
| 106 | self.rrev = None | ||
| 107 | if self.rrev: | ||
| 108 | self.rsum = self.proxy.sum(self.file) | ||
| 109 | else: | ||
| 110 | self.rsum = None | ||
| 111 | if self.eseen: | ||
| 112 | self.getesum() | ||
| 113 | self.rseen = 1 | ||
| 114 | |||
| 115 | def getesum(self): | ||
| 116 | if self.erev == self.rrev: | ||
| 117 | self.esum = self.rsum | ||
| 118 | elif self.erev: | ||
| 119 | name = (self.file, self.erev) | ||
| 120 | self.esum = self.proxy.sum(name) | ||
| 121 | else: | ||
| 122 | self.esum = None | ||
| 123 | |||
| 124 | def putentry(self): | ||
| 125 | """Return a line suitable for inclusion in CVS/Entries. | ||
| 126 | |||
| 127 | The returned line is terminated by a newline. | ||
| 128 | If no entry should be written for this file, | ||
| 129 | return "". | ||
| 130 | """ | ||
| 131 | if not self.eseen: | ||
| 132 | return "" | ||
| 133 | |||
| 134 | rev = self.erev or '0' | ||
| 135 | if self.edeleted: | ||
| 136 | rev = '-' + rev | ||
| 137 | if self.enew: | ||
| 138 | dates = 'Initial ' + self.file | ||
| 139 | else: | ||
| 140 | dates = gmctime(self.ectime) + ' ' + \ | ||
| 141 | gmctime(self.emtime) | ||
| 142 | return "/%s/%s/%s/%s/\n" % ( | ||
| 143 | self.file, | ||
| 144 | rev, | ||
| 145 | dates, | ||
| 146 | self.extra) | ||
| 147 | |||
| 148 | def report(self): | ||
| 149 | print '-'*50 | ||
| 150 | def r(key, repr=repr, self=self): | ||
| 151 | try: | ||
| 152 | value = repr(getattr(self, key)) | ||
| 153 | except AttributeError: | ||
| 154 | value = "?" | ||
| 155 | print "%-15s:" % key, value | ||
| 156 | r("file") | ||
| 157 | if self.lseen: | ||
| 158 | r("lsum", hexify) | ||
| 159 | r("lctime", gmctime) | ||
| 160 | r("lmtime", gmctime) | ||
| 161 | if self.eseen: | ||
| 162 | r("erev") | ||
| 163 | r("enew") | ||
| 164 | r("edeleted") | ||
| 165 | r("ectime", gmctime) | ||
| 166 | r("emtime", gmctime) | ||
| 167 | if self.rseen: | ||
| 168 | r("rrev") | ||
| 169 | r("rsum", hexify) | ||
| 170 | if self.eseen: | ||
| 171 | r("esum", hexify) | ||
| 172 | |||
| 173 | |||
| 174 | class CVS: | ||
| 175 | |||
| 176 | """Represent the contents of a CVS admin file (and more). | ||
| 177 | |||
| 178 | Class variables: | ||
| 179 | |||
| 180 | FileClass -- the class to be instantiated for entries | ||
| 181 | (this should be derived from class File above) | ||
| 182 | IgnoreList -- shell patterns for local files to be ignored | ||
| 183 | |||
| 184 | Instance variables: | ||
| 185 | |||
| 186 | entries -- a dictionary containing File instances keyed by | ||
| 187 | their file name | ||
| 188 | proxy -- an RCSProxy instance, or None | ||
| 189 | """ | ||
| 190 | |||
| 191 | FileClass = File | ||
| 192 | |||
| 193 | IgnoreList = ['.*', '@*', ',*', '*~', '*.o', '*.a', '*.so', '*.pyc'] | ||
| 194 | |||
| 195 | def __init__(self): | ||
| 196 | self.entries = {} | ||
| 197 | self.proxy = None | ||
| 198 | |||
| 199 | def setproxy(self, proxy): | ||
| 200 | if proxy is self.proxy: | ||
| 201 | return | ||
| 202 | self.proxy = proxy | ||
| 203 | for e in self.entries.values(): | ||
| 204 | e.rseen = 0 | ||
| 205 | |||
| 206 | def getentries(self): | ||
| 207 | """Read the contents of CVS/Entries""" | ||
| 208 | self.entries = {} | ||
| 209 | f = self.cvsopen("Entries") | ||
| 210 | while 1: | ||
| 211 | line = f.readline() | ||
| 212 | if not line: break | ||
| 213 | e = self.FileClass() | ||
| 214 | e.getentry(line) | ||
| 215 | self.entries[e.file] = e | ||
| 216 | f.close() | ||
| 217 | |||
| 218 | def putentries(self): | ||
| 219 | """Write CVS/Entries back""" | ||
| 220 | f = self.cvsopen("Entries", 'w') | ||
| 221 | for e in self.values(): | ||
| 222 | f.write(e.putentry()) | ||
| 223 | f.close() | ||
| 224 | |||
| 225 | def getlocalfiles(self): | ||
| 226 | list = self.entries.keys() | ||
| 227 | addlist = os.listdir(os.curdir) | ||
| 228 | for name in addlist: | ||
| 229 | if name in list: | ||
| 230 | continue | ||
| 231 | if not self.ignored(name): | ||
| 232 | list.append(name) | ||
| 233 | list.sort() | ||
| 234 | for file in list: | ||
| 235 | try: | ||
| 236 | e = self.entries[file] | ||
| 237 | except KeyError: | ||
| 238 | e = self.entries[file] = self.FileClass(file) | ||
| 239 | e.getlocal() | ||
| 240 | |||
| 241 | def getremotefiles(self, proxy = None): | ||
| 242 | if proxy: | ||
| 243 | self.proxy = proxy | ||
| 244 | if not self.proxy: | ||
| 245 | raise RuntimeError, "no RCS proxy" | ||
| 246 | addlist = self.proxy.listfiles() | ||
| 247 | for file in addlist: | ||
| 248 | try: | ||
| 249 | e = self.entries[file] | ||
| 250 | except KeyError: | ||
| 251 | e = self.entries[file] = self.FileClass(file) | ||
| 252 | e.getremote(self.proxy) | ||
| 253 | |||
| 254 | def report(self): | ||
| 255 | for e in self.values(): | ||
| 256 | e.report() | ||
| 257 | print '-'*50 | ||
| 258 | |||
| 259 | def keys(self): | ||
| 260 | keys = self.entries.keys() | ||
| 261 | keys.sort() | ||
| 262 | return keys | ||
| 263 | |||
| 264 | def values(self): | ||
| 265 | def value(key, self=self): | ||
| 266 | return self.entries[key] | ||
| 267 | return map(value, self.keys()) | ||
| 268 | |||
| 269 | def items(self): | ||
| 270 | def item(key, self=self): | ||
| 271 | return (key, self.entries[key]) | ||
| 272 | return map(item, self.keys()) | ||
| 273 | |||
| 274 | def cvsexists(self, file): | ||
| 275 | file = os.path.join("CVS", file) | ||
| 276 | return os.path.exists(file) | ||
| 277 | |||
| 278 | def cvsopen(self, file, mode = 'r'): | ||
| 279 | file = os.path.join("CVS", file) | ||
| 280 | if 'r' not in mode: | ||
| 281 | self.backup(file) | ||
| 282 | return open(file, mode) | ||
| 283 | |||
| 284 | def backup(self, file): | ||
| 285 | if os.path.isfile(file): | ||
| 286 | bfile = file + '~' | ||
| 287 | try: os.unlink(bfile) | ||
| 288 | except os.error: pass | ||
| 289 | os.rename(file, bfile) | ||
| 290 | |||
| 291 | def ignored(self, file): | ||
| 292 | if os.path.isdir(file): return True | ||
| 293 | for pat in self.IgnoreList: | ||
| 294 | if fnmatch.fnmatch(file, pat): return True | ||
| 295 | return False | ||
| 296 | |||
| 297 | |||
| 298 | # hexify and unhexify are useful to print MD5 checksums in hex format | ||
| 299 | |||
| 300 | hexify_format = '%02x' * 16 | ||
| 301 | def hexify(sum): | ||
| 302 | "Return a hex representation of a 16-byte string (e.g. an MD5 digest)" | ||
| 303 | if sum is None: | ||
| 304 | return "None" | ||
| 305 | return hexify_format % tuple(map(ord, sum)) | ||
| 306 | |||
| 307 | def unhexify(hexsum): | ||
| 308 | "Return the original from a hexified string" | ||
| 309 | if hexsum == "None": | ||
| 310 | return None | ||
| 311 | sum = '' | ||
| 312 | for i in range(0, len(hexsum), 2): | ||
| 313 | sum = sum + chr(string.atoi(hexsum[i:i+2], 16)) | ||
| 314 | return sum | ||
| 315 | |||
| 316 | |||
| 317 | unctime_monthmap = {} | ||
| 318 | def unctime(date): | ||
| 319 | if date == "None": return None | ||
| 320 | if not unctime_monthmap: | ||
| 321 | months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', | ||
| 322 | 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] | ||
| 323 | i = 0 | ||
| 324 | for m in months: | ||
| 325 | i = i+1 | ||
| 326 | unctime_monthmap[m] = i | ||
| 327 | words = string.split(date) # Day Mon DD HH:MM:SS YEAR | ||
| 328 | year = string.atoi(words[4]) | ||
| 329 | month = unctime_monthmap[words[1]] | ||
| 330 | day = string.atoi(words[2]) | ||
| 331 | [hh, mm, ss] = map(string.atoi, string.splitfields(words[3], ':')) | ||
| 332 | ss = ss - time.timezone | ||
| 333 | return time.mktime((year, month, day, hh, mm, ss, 0, 0, 0)) | ||
| 334 | |||
| 335 | def gmctime(t): | ||
| 336 | if t is None: return "None" | ||
| 337 | return time.asctime(time.gmtime(t)) | ||
| 338 | |||
| 339 | def test_unctime(): | ||
| 340 | now = int(time.time()) | ||
| 341 | t = time.gmtime(now) | ||
| 342 | at = time.asctime(t) | ||
| 343 | print 'GMT', now, at | ||
| 344 | print 'timezone', time.timezone | ||
| 345 | print 'local', time.ctime(now) | ||
| 346 | u = unctime(at) | ||
| 347 | print 'unctime()', u | ||
| 348 | gu = time.gmtime(u) | ||
| 349 | print '->', gu | ||
| 350 | print time.asctime(gu) | ||
| 351 | |||
| 352 | def test(): | ||
| 353 | x = CVS() | ||
| 354 | x.getentries() | ||
| 355 | x.getlocalfiles() | ||
| 356 | ## x.report() | ||
| 357 | import rcsclient | ||
| 358 | proxy = rcsclient.openrcsclient() | ||
| 359 | x.getremotefiles(proxy) | ||
| 360 | x.report() | ||
| 361 | |||
| 362 | |||
| 363 | if __name__ == "__main__": | ||
| 364 | test() | ||
| @@ -0,0 +1,280 @@ | |||
| 1 | """CVS locking algorithm. | ||
| 2 | |||
| 3 | CVS locking strategy | ||
| 4 | ==================== | ||
| 5 | |||
| 6 | As reverse engineered from the CVS 1.3 sources (file lock.c): | ||
| 7 | |||
| 8 | - Locking is done on a per repository basis (but a process can hold | ||
| 9 | write locks for multiple directories); all lock files are placed in | ||
| 10 | the repository and have names beginning with "#cvs.". | ||
| 11 | |||
| 12 | - Before even attempting to lock, a file "#cvs.tfl.<pid>" is created | ||
| 13 | (and removed again), to test that we can write the repository. [The | ||
| 14 | algorithm can still be fooled (1) if the repository's mode is changed | ||
| 15 | while attempting to lock; (2) if this file exists and is writable but | ||
| 16 | the directory is not.] | ||
| 17 | |||
| 18 | - While creating the actual read/write lock files (which may exist for | ||
| 19 | a long time), a "meta-lock" is held. The meta-lock is a directory | ||
| 20 | named "#cvs.lock" in the repository. The meta-lock is also held while | ||
| 21 | a write lock is held. | ||
| 22 | |||
| 23 | - To set a read lock: | ||
| 24 | |||
| 25 | - acquire the meta-lock | ||
| 26 | - create the file "#cvs.rfl.<pid>" | ||
| 27 | - release the meta-lock | ||
| 28 | |||
| 29 | - To set a write lock: | ||
| 30 | |||
| 31 | - acquire the meta-lock | ||
| 32 | - check that there are no files called "#cvs.rfl.*" | ||
| 33 | - if there are, release the meta-lock, sleep, try again | ||
| 34 | - create the file "#cvs.wfl.<pid>" | ||
| 35 | |||
| 36 | - To release a write lock: | ||
| 37 | |||
| 38 | - remove the file "#cvs.wfl.<pid>" | ||
| 39 | - rmdir the meta-lock | ||
| 40 | |||
| 41 | - To release a read lock: | ||
| 42 | |||
| 43 | - remove the file "#cvs.rfl.<pid>" | ||
| 44 | |||
| 45 | |||
| 46 | Additional notes | ||
| 47 | ---------------- | ||
| 48 | |||
| 49 | - A process should read-lock at most one repository at a time. | ||
| 50 | |||
| 51 | - A process may write-lock as many repositories as it wishes (to avoid | ||
| 52 | deadlocks, I presume it should always lock them top-down in the | ||
| 53 | directory hierarchy). | ||
| 54 | |||
| 55 | - A process should make sure it removes all its lock files and | ||
| 56 | directories when it crashes. | ||
| 57 | |||
| 58 | - Limitation: one user id should not be committing files into the same | ||
| 59 | repository at the same time. | ||
| 60 | |||
| 61 | |||
| 62 | Turn this into Python code | ||
| 63 | -------------------------- | ||
| 64 | |||
| 65 | rl = ReadLock(repository, waittime) | ||
| 66 | |||
| 67 | wl = WriteLock(repository, waittime) | ||
| 68 | |||
| 69 | list = MultipleWriteLock([repository1, repository2, ...], waittime) | ||
| 70 | |||
| 71 | """ | ||
| 72 | |||
| 73 | |||
| 74 | import os | ||
| 75 | import time | ||
| 76 | import stat | ||
| 77 | import pwd | ||
| 78 | |||
| 79 | |||
| 80 | # Default wait time | ||
| 81 | DELAY = 10 | ||
| 82 | |||
| 83 | |||
| 84 | # XXX This should be the same on all Unix versions | ||
| 85 | EEXIST = 17 | ||
| 86 | |||
| 87 | |||
| 88 | # Files used for locking (must match cvs.h in the CVS sources) | ||
| 89 | CVSLCK = "#cvs.lck" | ||
| 90 | CVSRFL = "#cvs.rfl." | ||
| 91 | CVSWFL = "#cvs.wfl." | ||
| 92 | |||
| 93 | |||
| 94 | class Error: | ||
| 95 | |||
| 96 | def __init__(self, msg): | ||
| 97 | self.msg = msg | ||
| 98 | |||
| 99 | def __repr__(self): | ||
| 100 | return repr(self.msg) | ||
| 101 | |||
| 102 | def __str__(self): | ||
| 103 | return str(self.msg) | ||
| 104 | |||
| 105 | |||
| 106 | class Locked(Error): | ||
| 107 | pass | ||
| 108 | |||
| 109 | |||
| 110 | class Lock: | ||
| 111 | |||
| 112 | def __init__(self, repository = ".", delay = DELAY): | ||
| 113 | self.repository = repository | ||
| 114 | self.delay = delay | ||
| 115 | self.lockdir = None | ||
| 116 | self.lockfile = None | ||
| 117 | pid = repr(os.getpid()) | ||
| 118 | self.cvslck = self.join(CVSLCK) | ||
| 119 | self.cvsrfl = self.join(CVSRFL + pid) | ||
| 120 | self.cvswfl = self.join(CVSWFL + pid) | ||
| 121 | |||
| 122 | def __del__(self): | ||
| 123 | print "__del__" | ||
| 124 | self.unlock() | ||
| 125 | |||
| 126 | def setlockdir(self): | ||
| 127 | while 1: | ||
| 128 | try: | ||
| 129 | self.lockdir = self.cvslck | ||
| 130 | os.mkdir(self.cvslck, 0777) | ||
| 131 | return | ||
| 132 | except os.error, msg: | ||
| 133 | self.lockdir = None | ||
| 134 | if msg[0] == EEXIST: | ||
| 135 | try: | ||
| 136 | st = os.stat(self.cvslck) | ||
| 137 | except os.error: | ||
| 138 | continue | ||
| 139 | self.sleep(st) | ||
| 140 | continue | ||
| 141 | raise Error("failed to lock %s: %s" % ( | ||
| 142 | self.repository, msg)) | ||
| 143 | |||
| 144 | def unlock(self): | ||
| 145 | self.unlockfile() | ||
| 146 | self.unlockdir() | ||
| 147 | |||
| 148 | def unlockfile(self): | ||
| 149 | if self.lockfile: | ||
| 150 | print "unlink", self.lockfile | ||
| 151 | try: | ||
| 152 | os.unlink(self.lockfile) | ||
| 153 | except os.error: | ||
| 154 | pass | ||
| 155 | self.lockfile = None | ||
| 156 | |||
| 157 | def unlockdir(self): | ||
| 158 | if self.lockdir: | ||
| 159 | print "rmdir", self.lockdir | ||
| 160 | try: | ||
| 161 | os.rmdir(self.lockdir) | ||
| 162 | except os.error: | ||
| 163 | pass | ||
| 164 | self.lockdir = None | ||
| 165 | |||
| 166 | def sleep(self, st): | ||
| 167 | sleep(st, self.repository, self.delay) | ||
| 168 | |||
| 169 | def join(self, name): | ||
| 170 | return os.path.join(self.repository, name) | ||
| 171 | |||
| 172 | |||
| 173 | def sleep(st, repository, delay): | ||
| 174 | if delay <= 0: | ||
| 175 | raise Locked(st) | ||
| 176 | uid = st[stat.ST_UID] | ||
| 177 | try: | ||
| 178 | pwent = pwd.getpwuid(uid) | ||
| 179 | user = pwent[0] | ||
| 180 | except KeyError: | ||
| 181 | user = "uid %d" % uid | ||
| 182 | print "[%s]" % time.ctime(time.time())[11:19], | ||
| 183 | print "Waiting for %s's lock in" % user, repository | ||
| 184 | time.sleep(delay) | ||
| 185 | |||
| 186 | |||
| 187 | class ReadLock(Lock): | ||
| 188 | |||
| 189 | def __init__(self, repository, delay = DELAY): | ||
| 190 | Lock.__init__(self, repository, delay) | ||
| 191 | ok = 0 | ||
| 192 | try: | ||
| 193 | self.setlockdir() | ||
| 194 | self.lockfile = self.cvsrfl | ||
| 195 | fp = open(self.lockfile, 'w') | ||
| 196 | fp.close() | ||
| 197 | ok = 1 | ||
| 198 | finally: | ||
| 199 | if not ok: | ||
| 200 | self.unlockfile() | ||
| 201 | self.unlockdir() | ||
| 202 | |||
| 203 | |||
| 204 | class WriteLock(Lock): | ||
| 205 | |||
| 206 | def __init__(self, repository, delay = DELAY): | ||
| 207 | Lock.__init__(self, repository, delay) | ||
| 208 | self.setlockdir() | ||
| 209 | while 1: | ||
| 210 | uid = self.readers_exist() | ||
| 211 | if not uid: | ||
| 212 | break | ||
| 213 | self.unlockdir() | ||
| 214 | self.sleep(uid) | ||
| 215 | self.lockfile = self.cvswfl | ||
| 216 | fp = open(self.lockfile, 'w') | ||
| 217 | fp.close() | ||
| 218 | |||
| 219 | def readers_exist(self): | ||
| 220 | n = len(CVSRFL) | ||
| 221 | for name in os.listdir(self.repository): | ||
| 222 | if name[:n] == CVSRFL: | ||
| 223 | try: | ||
| 224 | st = os.stat(self.join(name)) | ||
| 225 | except os.error: | ||
| 226 | continue | ||
| 227 | return st | ||
| 228 | return None | ||
| 229 | |||
| 230 | |||
| 231 | def MultipleWriteLock(repositories, delay = DELAY): | ||
| 232 | while 1: | ||
| 233 | locks = [] | ||
| 234 | for r in repositories: | ||
| 235 | try: | ||
| 236 | locks.append(WriteLock(r, 0)) | ||
| 237 | except Locked, instance: | ||
| 238 | del locks | ||
| 239 | break | ||
| 240 | else: | ||
| 241 | break | ||
| 242 | sleep(instance.msg, r, delay) | ||
| 243 | return list | ||
| 244 | |||
| 245 | |||
| 246 | def test(): | ||
| 247 | import sys | ||
| 248 | if sys.argv[1:]: | ||
| 249 | repository = sys.argv[1] | ||
| 250 | else: | ||
| 251 | repository = "." | ||
| 252 | rl = None | ||
| 253 | wl = None | ||
| 254 | try: | ||
| 255 | print "attempting write lock ..." | ||
| 256 | wl = WriteLock(repository) | ||
| 257 | print "got it." | ||
| 258 | wl.unlock() | ||
| 259 | print "attempting read lock ..." | ||
| 260 | rl = ReadLock(repository) | ||
| 261 | print "got it." | ||
| 262 | rl.unlock() | ||
| 263 | finally: | ||
| 264 | print [1] | ||
| 265 | sys.exc_traceback = None | ||
| 266 | print [2] | ||
| 267 | if rl: | ||
| 268 | rl.unlock() | ||
| 269 | print [3] | ||
| 270 | if wl: | ||
| 271 | wl.unlock() | ||
| 272 | print [4] | ||
| 273 | rl = None | ||
| 274 | print [5] | ||
| 275 | wl = None | ||
| 276 | print [6] | ||
| 277 | |||
| 278 | |||
| 279 | if __name__ == '__main__': | ||
| 280 | test() | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | import sys | ||
| 2 | import string | ||
| 3 | import rcvs | ||
| 4 | |||
| 5 | def main(): | ||
| 6 | while 1: | ||
| 7 | try: | ||
| 8 | line = raw_input('$ ') | ||
| 9 | except EOFError: | ||
| 10 | break | ||
| 11 | words = string.split(line) | ||
| 12 | if not words: | ||
| 13 | continue | ||
| 14 | if words[0] != 'rcvs': | ||
| 15 | words.insert(0, 'rcvs') | ||
| 16 | sys.argv = words | ||
| 17 | rcvs.main() | ||
| 18 | |||
| 19 | main() | ||
| @@ -0,0 +1,109 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Turn a pile of RCS log output into ChangeLog file entries. | ||
| 4 | |||
| 5 | """ | ||
| 6 | |||
| 7 | import sys | ||
| 8 | import string | ||
| 9 | import re | ||
| 10 | import getopt | ||
| 11 | import time | ||
| 12 | |||
| 13 | def main(): | ||
| 14 | args = sys.argv[1:] | ||
| 15 | opts, args = getopt.getopt(args, 'p:') | ||
| 16 | prefix = '' | ||
| 17 | for o, a in opts: | ||
| 18 | if p == '-p': prefix = a | ||
| 19 | |||
| 20 | f = sys.stdin | ||
| 21 | allrevs = [] | ||
| 22 | while 1: | ||
| 23 | file = getnextfile(f) | ||
| 24 | if not file: break | ||
| 25 | revs = [] | ||
| 26 | while 1: | ||
| 27 | rev = getnextrev(f, file) | ||
| 28 | if not rev: | ||
| 29 | break | ||
| 30 | revs.append(rev) | ||
| 31 | if revs: | ||
| 32 | allrevs[len(allrevs):] = revs | ||
| 33 | allrevs.sort() | ||
| 34 | allrevs.reverse() | ||
| 35 | for rev in allrevs: | ||
| 36 | formatrev(rev, prefix) | ||
| 37 | |||
| 38 | parsedateprog = re.compile( | ||
| 39 | '^date: ([0-9]+)/([0-9]+)/([0-9]+) ' + | ||
| 40 | '([0-9]+):([0-9]+):([0-9]+); author: ([^ ;]+)') | ||
| 41 | |||
| 42 | authormap = { | ||
| 43 | 'guido': 'Guido van Rossum <guido@cnri.reston.va.us>', | ||
| 44 | 'jack': 'Jack Jansen <jack@cwi.nl>', | ||
| 45 | 'sjoerd': 'Sjoerd Mullender <sjoerd@cwi.nl>', | ||
| 46 | } | ||
| 47 | |||
| 48 | def formatrev(rev, prefix): | ||
| 49 | dateline, file, revline, log = rev | ||
| 50 | if parsedateprog.match(dateline) >= 0: | ||
| 51 | fields = parsedateprog.group(1, 2, 3, 4, 5, 6) | ||
| 52 | author = parsedateprog.group(7) | ||
| 53 | if authormap.has_key(author): author = authormap[author] | ||
| 54 | tfields = map(string.atoi, fields) + [0, 0, 0] | ||
| 55 | tfields[5] = tfields[5] - time.timezone | ||
| 56 | t = time.mktime(tuple(tfields)) | ||
| 57 | print time.ctime(t), '', author | ||
| 58 | words = string.split(log) | ||
| 59 | words[:0] = ['*', prefix + file + ':'] | ||
| 60 | maxcol = 72-8 | ||
| 61 | col = maxcol | ||
| 62 | for word in words: | ||
| 63 | if col > 0 and col + len(word) >= maxcol: | ||
| 64 | |||
| 65 | print '\t' + word, | ||
| 66 | col = -1 | ||
| 67 | else: | ||
| 68 | print word, | ||
| 69 | col = col + 1 + len(word) | ||
| 70 | |||
| 71 | |||
| 72 | |||
| 73 | startprog = re.compile("^Working file: (.*)$") | ||
| 74 | |||
| 75 | def getnextfile(f): | ||
| 76 | while 1: | ||
| 77 | line = f.readline() | ||
| 78 | if not line: return None | ||
| 79 | if startprog.match(line) >= 0: | ||
| 80 | file = startprog.group(1) | ||
| 81 | # Skip until first revision | ||
| 82 | while 1: | ||
| 83 | line = f.readline() | ||
| 84 | if not line: return None | ||
| 85 | if line[:10] == '='*10: return None | ||
| 86 | if line[:10] == '-'*10: break | ||
| 87 | ## print "Skipped", line, | ||
| 88 | return file | ||
| 89 | ## else: | ||
| 90 | ## print "Ignored", line, | ||
| 91 | |||
| 92 | def getnextrev(f, file): | ||
| 93 | # This is called when we are positioned just after a '---' separator | ||
| 94 | revline = f.readline() | ||
| 95 | dateline = f.readline() | ||
| 96 | log = '' | ||
| 97 | while 1: | ||
| 98 | line = f.readline() | ||
| 99 | if not line: break | ||
| 100 | if line[:10] == '='*10: | ||
| 101 | # Ignore the *last* log entry for each file since it | ||
| 102 | # is the revision since which we are logging. | ||
| 103 | return None | ||
| 104 | if line[:10] == '-'*10: break | ||
| 105 | log = log + line | ||
| 106 | return dateline, file, revline, log | ||
| 107 | |||
| 108 | if __name__ == '__main__': | ||
| 109 | main() | ||
| @@ -0,0 +1,33 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # -*- python -*- | ||
| 3 | # | ||
| 4 | # guido's version, from rcsbump,v 1.2 1995/06/22 21:27:27 bwarsaw Exp | ||
| 5 | # | ||
| 6 | # Python script for bumping up an RCS major revision number. | ||
| 7 | |||
| 8 | import sys | ||
| 9 | import re | ||
| 10 | import rcslib | ||
| 11 | import string | ||
| 12 | |||
| 13 | WITHLOCK = 1 | ||
| 14 | majorrev_re = re.compile('^[0-9]+') | ||
| 15 | |||
| 16 | dir = rcslib.RCS() | ||
| 17 | |||
| 18 | if sys.argv[1:]: | ||
| 19 | files = sys.argv[1:] | ||
| 20 | else: | ||
| 21 | files = dir.listfiles() | ||
| 22 | |||
| 23 | for file in files: | ||
| 24 | # get the major revnumber of the file | ||
| 25 | headbranch = dir.info(file)['head'] | ||
| 26 | majorrev_re.match(headbranch) | ||
| 27 | majorrev = string.atoi(majorrev_re.group(0)) + 1 | ||
| 28 | |||
| 29 | if not dir.islocked(file): | ||
| 30 | dir.checkout(file, WITHLOCK) | ||
| 31 | |||
| 32 | msg = "Bumping major revision number (to %d)" % majorrev | ||
| 33 | dir.checkin((file, "%s.0" % majorrev), msg, "-f") | ||
| @@ -0,0 +1,71 @@ | |||
| 1 | """Customize this file to change the default client etc. | ||
| 2 | |||
| 3 | (In general, it is probably be better to make local operation the | ||
| 4 | default and to require something like an RCSSERVER environment | ||
| 5 | variable to enable remote operation.) | ||
| 6 | |||
| 7 | """ | ||
| 8 | |||
| 9 | import string | ||
| 10 | import os | ||
| 11 | |||
| 12 | # These defaults don't belong here -- they should be taken from the | ||
| 13 | # environment or from a hidden file in the current directory | ||
| 14 | |||
| 15 | HOST = 'voorn.cwi.nl' | ||
| 16 | PORT = 4127 | ||
| 17 | VERBOSE = 1 | ||
| 18 | LOCAL = 0 | ||
| 19 | |||
| 20 | import client | ||
| 21 | |||
| 22 | |||
| 23 | class RCSProxyClient(client.SecureClient): | ||
| 24 | |||
| 25 | def __init__(self, address, verbose = client.VERBOSE): | ||
| 26 | client.SecureClient.__init__(self, address, verbose) | ||
| 27 | |||
| 28 | |||
| 29 | def openrcsclient(opts = []): | ||
| 30 | "open an RCSProxy client based on a list of options returned by getopt" | ||
| 31 | import RCSProxy | ||
| 32 | host = HOST | ||
| 33 | port = PORT | ||
| 34 | verbose = VERBOSE | ||
| 35 | local = LOCAL | ||
| 36 | directory = None | ||
| 37 | for o, a in opts: | ||
| 38 | if o == '-h': | ||
| 39 | host = a | ||
| 40 | if ':' in host: | ||
| 41 | i = string.find(host, ':') | ||
| 42 | host, p = host[:i], host[i+1:] | ||
| 43 | if p: | ||
| 44 | port = string.atoi(p) | ||
| 45 | if o == '-p': | ||
| 46 | port = string.atoi(a) | ||
| 47 | if o == '-d': | ||
| 48 | directory = a | ||
| 49 | if o == '-v': | ||
| 50 | verbose = verbose + 1 | ||
| 51 | if o == '-q': | ||
| 52 | verbose = 0 | ||
| 53 | if o == '-L': | ||
| 54 | local = 1 | ||
| 55 | if local: | ||
| 56 | import RCSProxy | ||
| 57 | x = RCSProxy.RCSProxyLocal() | ||
| 58 | else: | ||
| 59 | address = (host, port) | ||
| 60 | x = RCSProxyClient(address, verbose) | ||
| 61 | if not directory: | ||
| 62 | try: | ||
| 63 | directory = open(os.path.join("CVS", "Repository")).readline() | ||
| 64 | except IOError: | ||
| 65 | pass | ||
| 66 | else: | ||
| 67 | if directory[-1] == '\n': | ||
| 68 | directory = directory[:-1] | ||
| 69 | if directory: | ||
| 70 | x.cd(directory) | ||
| 71 | return x | ||
| @@ -0,0 +1,334 @@ | |||
| 1 | """RCS interface module. | ||
| 2 | |||
| 3 | Defines the class RCS, which represents a directory with rcs version | ||
| 4 | files and (possibly) corresponding work files. | ||
| 5 | |||
| 6 | """ | ||
| 7 | |||
| 8 | |||
| 9 | import fnmatch | ||
| 10 | import os | ||
| 11 | import re | ||
| 12 | import string | ||
| 13 | import tempfile | ||
| 14 | |||
| 15 | |||
| 16 | class RCS: | ||
| 17 | |||
| 18 | """RCS interface class (local filesystem version). | ||
| 19 | |||
| 20 | An instance of this class represents a directory with rcs version | ||
| 21 | files and (possible) corresponding work files. | ||
| 22 | |||
| 23 | Methods provide access to most rcs operations such as | ||
| 24 | checkin/checkout, access to the rcs metadata (revisions, logs, | ||
| 25 | branches etc.) as well as some filesystem operations such as | ||
| 26 | listing all rcs version files. | ||
| 27 | |||
| 28 | XXX BUGS / PROBLEMS | ||
| 29 | |||
| 30 | - The instance always represents the current directory so it's not | ||
| 31 | very useful to have more than one instance around simultaneously | ||
| 32 | |||
| 33 | """ | ||
| 34 | |||
| 35 | # Characters allowed in work file names | ||
| 36 | okchars = string.ascii_letters + string.digits + '-_=+' | ||
| 37 | |||
| 38 | def __init__(self): | ||
| 39 | """Constructor.""" | ||
| 40 | pass | ||
| 41 | |||
| 42 | def __del__(self): | ||
| 43 | """Destructor.""" | ||
| 44 | pass | ||
| 45 | |||
| 46 | # --- Informational methods about a single file/revision --- | ||
| 47 | |||
| 48 | def log(self, name_rev, otherflags = ''): | ||
| 49 | """Return the full log text for NAME_REV as a string. | ||
| 50 | |||
| 51 | Optional OTHERFLAGS are passed to rlog. | ||
| 52 | |||
| 53 | """ | ||
| 54 | f = self._open(name_rev, 'rlog ' + otherflags) | ||
| 55 | data = f.read() | ||
| 56 | status = self._closepipe(f) | ||
| 57 | if status: | ||
| 58 | data = data + "%s: %s" % status | ||
| 59 | elif data[-1] == '\n': | ||
| 60 | data = data[:-1] | ||
| 61 | return data | ||
| 62 | |||
| 63 | def head(self, name_rev): | ||
| 64 | """Return the head revision for NAME_REV""" | ||
| 65 | dict = self.info(name_rev) | ||
| 66 | return dict['head'] | ||
| 67 | |||
| 68 | def info(self, name_rev): | ||
| 69 | """Return a dictionary of info (from rlog -h) for NAME_REV | ||
| 70 | |||
| 71 | The dictionary's keys are the keywords that rlog prints | ||
| 72 | (e.g. 'head' and its values are the corresponding data | ||
| 73 | (e.g. '1.3'). | ||
| 74 | |||
| 75 | XXX symbolic names and locks are not returned | ||
| 76 | |||
| 77 | """ | ||
| 78 | f = self._open(name_rev, 'rlog -h') | ||
| 79 | dict = {} | ||
| 80 | while 1: | ||
| 81 | line = f.readline() | ||
| 82 | if not line: break | ||
| 83 | if line[0] == '\t': | ||
| 84 | # XXX could be a lock or symbolic name | ||
| 85 | # Anything else? | ||
| 86 | continue | ||
| 87 | i = string.find(line, ':') | ||
| 88 | if i > 0: | ||
| 89 | key, value = line[:i], string.strip(line[i+1:]) | ||
| 90 | dict[key] = value | ||
| 91 | status = self._closepipe(f) | ||
| 92 | if status: | ||
| 93 | raise IOError, status | ||
| 94 | return dict | ||
| 95 | |||
| 96 | # --- Methods that change files --- | ||
| 97 | |||
| 98 | def lock(self, name_rev): | ||
| 99 | """Set an rcs lock on NAME_REV.""" | ||
| 100 | name, rev = self.checkfile(name_rev) | ||
| 101 | cmd = "rcs -l%s %s" % (rev, name) | ||
| 102 | return self._system(cmd) | ||
| 103 | |||
| 104 | def unlock(self, name_rev): | ||
| 105 | """Clear an rcs lock on NAME_REV.""" | ||
| 106 | name, rev = self.checkfile(name_rev) | ||
| 107 | cmd = "rcs -u%s %s" % (rev, name) | ||
| 108 | return self._system(cmd) | ||
| 109 | |||
| 110 | def checkout(self, name_rev, withlock=0, otherflags=""): | ||
| 111 | """Check out NAME_REV to its work file. | ||
| 112 | |||
| 113 | If optional WITHLOCK is set, check out locked, else unlocked. | ||
| 114 | |||
| 115 | The optional OTHERFLAGS is passed to co without | ||
| 116 | interpretation. | ||
| 117 | |||
| 118 | Any output from co goes to directly to stdout. | ||
| 119 | |||
| 120 | """ | ||
| 121 | name, rev = self.checkfile(name_rev) | ||
| 122 | if withlock: lockflag = "-l" | ||
| 123 | else: lockflag = "-u" | ||
| 124 | cmd = 'co %s%s %s %s' % (lockflag, rev, otherflags, name) | ||
| 125 | return self._system(cmd) | ||
| 126 | |||
| 127 | def checkin(self, name_rev, message=None, otherflags=""): | ||
| 128 | """Check in NAME_REV from its work file. | ||
| 129 | |||
| 130 | The optional MESSAGE argument becomes the checkin message | ||
| 131 | (default "<none>" if None); or the file description if this is | ||
| 132 | a new file. | ||
| 133 | |||
| 134 | The optional OTHERFLAGS argument is passed to ci without | ||
| 135 | interpretation. | ||
| 136 | |||
| 137 | Any output from ci goes to directly to stdout. | ||
| 138 | |||
| 139 | """ | ||
| 140 | name, rev = self._unmangle(name_rev) | ||
| 141 | new = not self.isvalid(name) | ||
| 142 | if not message: message = "<none>" | ||
| 143 | if message and message[-1] != '\n': | ||
| 144 | message = message + '\n' | ||
| 145 | lockflag = "-u" | ||
| 146 | if new: | ||
| 147 | f = tempfile.NamedTemporaryFile() | ||
| 148 | f.write(message) | ||
| 149 | f.flush() | ||
| 150 | cmd = 'ci %s%s -t%s %s %s' % \ | ||
| 151 | (lockflag, rev, f.name, otherflags, name) | ||
| 152 | else: | ||
| 153 | message = re.sub(r'([\"$`])', r'\\\1', message) | ||
| 154 | cmd = 'ci %s%s -m"%s" %s %s' % \ | ||
| 155 | (lockflag, rev, message, otherflags, name) | ||
| 156 | return self._system(cmd) | ||
| 157 | |||
| 158 | # --- Exported support methods --- | ||
| 159 | |||
| 160 | def listfiles(self, pat = None): | ||
| 161 | """Return a list of all version files matching optional PATTERN.""" | ||
| 162 | files = os.listdir(os.curdir) | ||
| 163 | files = filter(self._isrcs, files) | ||
| 164 | if os.path.isdir('RCS'): | ||
| 165 | files2 = os.listdir('RCS') | ||
| 166 | files2 = filter(self._isrcs, files2) | ||
| 167 | files = files + files2 | ||
| 168 | files = map(self.realname, files) | ||
| 169 | return self._filter(files, pat) | ||
| 170 | |||
| 171 | def isvalid(self, name): | ||
| 172 | """Test whether NAME has a version file associated.""" | ||
| 173 | namev = self.rcsname(name) | ||
| 174 | return (os.path.isfile(namev) or | ||
| 175 | os.path.isfile(os.path.join('RCS', namev))) | ||
| 176 | |||
| 177 | def rcsname(self, name): | ||
| 178 | """Return the pathname of the version file for NAME. | ||
| 179 | |||
| 180 | The argument can be a work file name or a version file name. | ||
| 181 | If the version file does not exist, the name of the version | ||
| 182 | file that would be created by "ci" is returned. | ||
| 183 | |||
| 184 | """ | ||
| 185 | if self._isrcs(name): namev = name | ||
| 186 | else: namev = name + ',v' | ||
| 187 | if os.path.isfile(namev): return namev | ||
| 188 | namev = os.path.join('RCS', os.path.basename(namev)) | ||
| 189 | if os.path.isfile(namev): return namev | ||
| 190 | if os.path.isdir('RCS'): | ||
| 191 | return os.path.join('RCS', namev) | ||
| 192 | else: | ||
| 193 | return namev | ||
| 194 | |||
| 195 | def realname(self, namev): | ||
| 196 | """Return the pathname of the work file for NAME. | ||
| 197 | |||
| 198 | The argument can be a work file name or a version file name. | ||
| 199 | If the work file does not exist, the name of the work file | ||
| 200 | that would be created by "co" is returned. | ||
| 201 | |||
| 202 | """ | ||
| 203 | if self._isrcs(namev): name = namev[:-2] | ||
| 204 | else: name = namev | ||
| 205 | if os.path.isfile(name): return name | ||
| 206 | name = os.path.basename(name) | ||
| 207 | return name | ||
| 208 | |||
| 209 | def islocked(self, name_rev): | ||
| 210 | """Test whether FILE (which must have a version file) is locked. | ||
| 211 | |||
| 212 | XXX This does not tell you which revision number is locked and | ||
| 213 | ignores any revision you may pass in (by virtue of using rlog | ||
| 214 | -L -R). | ||
| 215 | |||
| 216 | """ | ||
| 217 | f = self._open(name_rev, 'rlog -L -R') | ||
| 218 | line = f.readline() | ||
| 219 | status = self._closepipe(f) | ||
| 220 | if status: | ||
| 221 | raise IOError, status | ||
| 222 | if not line: return None | ||
| 223 | if line[-1] == '\n': | ||
| 224 | line = line[:-1] | ||
| 225 | return self.realname(name_rev) == self.realname(line) | ||
| 226 | |||
| 227 | def checkfile(self, name_rev): | ||
| 228 | """Normalize NAME_REV into a (NAME, REV) tuple. | ||
| 229 | |||
| 230 | Raise an exception if there is no corresponding version file. | ||
| 231 | |||
| 232 | """ | ||
| 233 | name, rev = self._unmangle(name_rev) | ||
| 234 | if not self.isvalid(name): | ||
| 235 | raise os.error, 'not an rcs file %r' % (name,) | ||
| 236 | return name, rev | ||
| 237 | |||
| 238 | # --- Internal methods --- | ||
| 239 | |||
| 240 | def _open(self, name_rev, cmd = 'co -p', rflag = '-r'): | ||
| 241 | """INTERNAL: open a read pipe to NAME_REV using optional COMMAND. | ||
| 242 | |||
| 243 | Optional FLAG is used to indicate the revision (default -r). | ||
| 244 | |||
| 245 | Default COMMAND is "co -p". | ||
| 246 | |||
| 247 | Return a file object connected by a pipe to the command's | ||
| 248 | output. | ||
| 249 | |||
| 250 | """ | ||
| 251 | name, rev = self.checkfile(name_rev) | ||
| 252 | namev = self.rcsname(name) | ||
| 253 | if rev: | ||
| 254 | cmd = cmd + ' ' + rflag + rev | ||
| 255 | return os.popen("%s %r" % (cmd, namev)) | ||
| 256 | |||
| 257 | def _unmangle(self, name_rev): | ||
| 258 | """INTERNAL: Normalize NAME_REV argument to (NAME, REV) tuple. | ||
| 259 | |||
| 260 | Raise an exception if NAME contains invalid characters. | ||
| 261 | |||
| 262 | A NAME_REV argument is either NAME string (implying REV='') or | ||
| 263 | a tuple of the form (NAME, REV). | ||
| 264 | |||
| 265 | """ | ||
| 266 | if type(name_rev) == type(''): | ||
| 267 | name_rev = name, rev = name_rev, '' | ||
| 268 | else: | ||
| 269 | name, rev = name_rev | ||
| 270 | for c in rev: | ||
| 271 | if c not in self.okchars: | ||
| 272 | raise ValueError, "bad char in rev" | ||
| 273 | return name_rev | ||
| 274 | |||
| 275 | def _closepipe(self, f): | ||
| 276 | """INTERNAL: Close PIPE and print its exit status if nonzero.""" | ||
| 277 | sts = f.close() | ||
| 278 | if not sts: return None | ||
| 279 | detail, reason = divmod(sts, 256) | ||
| 280 | if reason == 0: return 'exit', detail # Exit status | ||
| 281 | signal = reason&0x7F | ||
| 282 | if signal == 0x7F: | ||
| 283 | code = 'stopped' | ||
| 284 | signal = detail | ||
| 285 | else: | ||
| 286 | code = 'killed' | ||
| 287 | if reason&0x80: | ||
| 288 | code = code + '(coredump)' | ||
| 289 | return code, signal | ||
| 290 | |||
| 291 | def _system(self, cmd): | ||
| 292 | """INTERNAL: run COMMAND in a subshell. | ||
| 293 | |||
| 294 | Standard input for the command is taken from /dev/null. | ||
| 295 | |||
| 296 | Raise IOError when the exit status is not zero. | ||
| 297 | |||
| 298 | Return whatever the calling method should return; normally | ||
| 299 | None. | ||
| 300 | |||
| 301 | A derived class may override this method and redefine it to | ||
| 302 | capture stdout/stderr of the command and return it. | ||
| 303 | |||
| 304 | """ | ||
| 305 | cmd = cmd + " </dev/null" | ||
| 306 | sts = os.system(cmd) | ||
| 307 | if sts: raise IOError, "command exit status %d" % sts | ||
| 308 | |||
| 309 | def _filter(self, files, pat = None): | ||
| 310 | """INTERNAL: Return a sorted copy of the given list of FILES. | ||
| 311 | |||
| 312 | If a second PATTERN argument is given, only files matching it | ||
| 313 | are kept. No check for valid filenames is made. | ||
| 314 | |||
| 315 | """ | ||
| 316 | if pat: | ||
| 317 | def keep(name, pat = pat): | ||
| 318 | return fnmatch.fnmatch(name, pat) | ||
| 319 | files = filter(keep, files) | ||
| 320 | else: | ||
| 321 | files = files[:] | ||
| 322 | files.sort() | ||
| 323 | return files | ||
| 324 | |||
| 325 | def _remove(self, fn): | ||
| 326 | """INTERNAL: remove FILE without complaints.""" | ||
| 327 | try: | ||
| 328 | os.unlink(fn) | ||
| 329 | except os.error: | ||
| 330 | pass | ||
| 331 | |||
| 332 | def _isrcs(self, name): | ||
| 333 | """INTERNAL: Test whether NAME ends in ',v'.""" | ||
| 334 | return name[-2:] == ',v' | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | import addpack | ||
| 4 | addpack.addpack('/home/guido/src/python/Demo/pdist') | ||
| 5 | |||
| 6 | import rcvs | ||
| 7 | |||
| 8 | rcvs.main() | ||
| @@ -0,0 +1,477 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Remote CVS -- command line interface""" | ||
| 4 | |||
| 5 | # XXX To do: | ||
| 6 | # | ||
| 7 | # Bugs: | ||
| 8 | # - if the remote file is deleted, "rcvs update" will fail | ||
| 9 | # | ||
| 10 | # Functionality: | ||
| 11 | # - cvs rm | ||
| 12 | # - descend into directories (alraedy done for update) | ||
| 13 | # - conflict resolution | ||
| 14 | # - other relevant commands? | ||
| 15 | # - branches | ||
| 16 | # | ||
| 17 | # - Finesses: | ||
| 18 | # - retain file mode's x bits | ||
| 19 | # - complain when "nothing known about filename" | ||
| 20 | # - edit log message the way CVS lets you edit it | ||
| 21 | # - cvs diff -rREVA -rREVB | ||
| 22 | # - send mail the way CVS sends it | ||
| 23 | # | ||
| 24 | # Performance: | ||
| 25 | # - cache remote checksums (for every revision ever seen!) | ||
| 26 | # - translate symbolic revisions to numeric revisions | ||
| 27 | # | ||
| 28 | # Reliability: | ||
| 29 | # - remote locking | ||
| 30 | # | ||
| 31 | # Security: | ||
| 32 | # - Authenticated RPC? | ||
| 33 | |||
| 34 | |||
| 35 | from cvslib import CVS, File | ||
| 36 | import md5 | ||
| 37 | import os | ||
| 38 | import string | ||
| 39 | import sys | ||
| 40 | from cmdfw import CommandFrameWork | ||
| 41 | |||
| 42 | |||
| 43 | DEF_LOCAL = 1 # Default -l | ||
| 44 | |||
| 45 | |||
| 46 | class MyFile(File): | ||
| 47 | |||
| 48 | def action(self): | ||
| 49 | """Return a code indicating the update status of this file. | ||
| 50 | |||
| 51 | The possible return values are: | ||
| 52 | |||
| 53 | '=' -- everything's fine | ||
| 54 | '0' -- file doesn't exist anywhere | ||
| 55 | '?' -- exists locally only | ||
| 56 | 'A' -- new locally | ||
| 57 | 'R' -- deleted locally | ||
| 58 | 'U' -- changed remotely, no changes locally | ||
| 59 | (includes new remotely or deleted remotely) | ||
| 60 | 'M' -- changed locally, no changes remotely | ||
| 61 | 'C' -- conflict: changed locally as well as remotely | ||
| 62 | (includes cases where the file has been added | ||
| 63 | or removed locally and remotely) | ||
| 64 | 'D' -- deleted remotely | ||
| 65 | 'N' -- new remotely | ||
| 66 | 'r' -- get rid of entry | ||
| 67 | 'c' -- create entry | ||
| 68 | 'u' -- update entry | ||
| 69 | |||
| 70 | (and probably others :-) | ||
| 71 | """ | ||
| 72 | if not self.lseen: | ||
| 73 | self.getlocal() | ||
| 74 | if not self.rseen: | ||
| 75 | self.getremote() | ||
| 76 | if not self.eseen: | ||
| 77 | if not self.lsum: | ||
| 78 | if not self.rsum: return '0' # Never heard of | ||
| 79 | else: | ||
| 80 | return 'N' # New remotely | ||
| 81 | else: # self.lsum | ||
| 82 | if not self.rsum: return '?' # Local only | ||
| 83 | # Local and remote, but no entry | ||
| 84 | if self.lsum == self.rsum: | ||
| 85 | return 'c' # Restore entry only | ||
| 86 | else: return 'C' # Real conflict | ||
| 87 | else: # self.eseen | ||
| 88 | if not self.lsum: | ||
| 89 | if self.edeleted: | ||
| 90 | if self.rsum: return 'R' # Removed | ||
| 91 | else: return 'r' # Get rid of entry | ||
| 92 | else: # not self.edeleted | ||
| 93 | if self.rsum: | ||
| 94 | print "warning:", | ||
| 95 | print self.file, | ||
| 96 | print "was lost" | ||
| 97 | return 'U' | ||
| 98 | else: return 'r' # Get rid of entry | ||
| 99 | else: # self.lsum | ||
| 100 | if not self.rsum: | ||
| 101 | if self.enew: return 'A' # New locally | ||
| 102 | else: return 'D' # Deleted remotely | ||
| 103 | else: # self.rsum | ||
| 104 | if self.enew: | ||
| 105 | if self.lsum == self.rsum: | ||
| 106 | return 'u' | ||
| 107 | else: | ||
| 108 | return 'C' | ||
| 109 | if self.lsum == self.esum: | ||
| 110 | if self.esum == self.rsum: | ||
| 111 | return '=' | ||
| 112 | else: | ||
| 113 | return 'U' | ||
| 114 | elif self.esum == self.rsum: | ||
| 115 | return 'M' | ||
| 116 | elif self.lsum == self.rsum: | ||
| 117 | return 'u' | ||
| 118 | else: | ||
| 119 | return 'C' | ||
| 120 | |||
| 121 | def update(self): | ||
| 122 | code = self.action() | ||
| 123 | if code == '=': return | ||
| 124 | print code, self.file | ||
| 125 | if code in ('U', 'N'): | ||
| 126 | self.get() | ||
| 127 | elif code == 'C': | ||
| 128 | print "%s: conflict resolution not yet implemented" % \ | ||
| 129 | self.file | ||
| 130 | elif code == 'D': | ||
| 131 | remove(self.file) | ||
| 132 | self.eseen = 0 | ||
| 133 | elif code == 'r': | ||
| 134 | self.eseen = 0 | ||
| 135 | elif code in ('c', 'u'): | ||
| 136 | self.eseen = 1 | ||
| 137 | self.erev = self.rrev | ||
| 138 | self.enew = 0 | ||
| 139 | self.edeleted = 0 | ||
| 140 | self.esum = self.rsum | ||
| 141 | self.emtime, self.ectime = os.stat(self.file)[-2:] | ||
| 142 | self.extra = '' | ||
| 143 | |||
| 144 | def commit(self, message = ""): | ||
| 145 | code = self.action() | ||
| 146 | if code in ('A', 'M'): | ||
| 147 | self.put(message) | ||
| 148 | return 1 | ||
| 149 | elif code == 'R': | ||
| 150 | print "%s: committing removes not yet implemented" % \ | ||
| 151 | self.file | ||
| 152 | elif code == 'C': | ||
| 153 | print "%s: conflict resolution not yet implemented" % \ | ||
| 154 | self.file | ||
| 155 | |||
| 156 | def diff(self, opts = []): | ||
| 157 | self.action() # To update lseen, rseen | ||
| 158 | flags = '' | ||
| 159 | rev = self.rrev | ||
| 160 | # XXX should support two rev options too! | ||
| 161 | for o, a in opts: | ||
| 162 | if o == '-r': | ||
| 163 | rev = a | ||
| 164 | else: | ||
| 165 | flags = flags + ' ' + o + a | ||
| 166 | if rev == self.rrev and self.lsum == self.rsum: | ||
| 167 | return | ||
| 168 | flags = flags[1:] | ||
| 169 | fn = self.file | ||
| 170 | data = self.proxy.get((fn, rev)) | ||
| 171 | sum = md5.new(data).digest() | ||
| 172 | if self.lsum == sum: | ||
| 173 | return | ||
| 174 | import tempfile | ||
| 175 | tf = tempfile.NamedTemporaryFile() | ||
| 176 | tf.write(data) | ||
| 177 | tf.flush() | ||
| 178 | print 'diff %s -r%s %s' % (flags, rev, fn) | ||
| 179 | sts = os.system('diff %s %s %s' % (flags, tf.name, fn)) | ||
| 180 | if sts: | ||
| 181 | print '='*70 | ||
| 182 | |||
| 183 | def commitcheck(self): | ||
| 184 | return self.action() != 'C' | ||
| 185 | |||
| 186 | def put(self, message = ""): | ||
| 187 | print "Checking in", self.file, "..." | ||
| 188 | data = open(self.file).read() | ||
| 189 | if not self.enew: | ||
| 190 | self.proxy.lock(self.file) | ||
| 191 | messages = self.proxy.put(self.file, data, message) | ||
| 192 | if messages: | ||
| 193 | print messages | ||
| 194 | self.setentry(self.proxy.head(self.file), self.lsum) | ||
| 195 | |||
| 196 | def get(self): | ||
| 197 | data = self.proxy.get(self.file) | ||
| 198 | f = open(self.file, 'w') | ||
| 199 | f.write(data) | ||
| 200 | f.close() | ||
| 201 | self.setentry(self.rrev, self.rsum) | ||
| 202 | |||
| 203 | def log(self, otherflags): | ||
| 204 | print self.proxy.log(self.file, otherflags) | ||
| 205 | |||
| 206 | def add(self): | ||
| 207 | self.eseen = 0 # While we're hacking... | ||
| 208 | self.esum = self.lsum | ||
| 209 | self.emtime, self.ectime = 0, 0 | ||
| 210 | self.erev = '' | ||
| 211 | self.enew = 1 | ||
| 212 | self.edeleted = 0 | ||
| 213 | self.eseen = 1 # Done | ||
| 214 | self.extra = '' | ||
| 215 | |||
| 216 | def setentry(self, erev, esum): | ||
| 217 | self.eseen = 0 # While we're hacking... | ||
| 218 | self.esum = esum | ||
| 219 | self.emtime, self.ectime = os.stat(self.file)[-2:] | ||
| 220 | self.erev = erev | ||
| 221 | self.enew = 0 | ||
| 222 | self.edeleted = 0 | ||
| 223 | self.eseen = 1 # Done | ||
| 224 | self.extra = '' | ||
| 225 | |||
| 226 | |||
| 227 | SENDMAIL = "/usr/lib/sendmail -t" | ||
| 228 | MAILFORM = """To: %s | ||
| 229 | Subject: CVS changes: %s | ||
| 230 | |||
| 231 | ...Message from rcvs... | ||
| 232 | |||
| 233 | Committed files: | ||
| 234 | %s | ||
| 235 | |||
| 236 | Log message: | ||
| 237 | %s | ||
| 238 | """ | ||
| 239 | |||
| 240 | |||
| 241 | class RCVS(CVS): | ||
| 242 | |||
| 243 | FileClass = MyFile | ||
| 244 | |||
| 245 | def __init__(self): | ||
| 246 | CVS.__init__(self) | ||
| 247 | |||
| 248 | def update(self, files): | ||
| 249 | for e in self.whichentries(files, 1): | ||
| 250 | e.update() | ||
| 251 | |||
| 252 | def commit(self, files, message = ""): | ||
| 253 | list = self.whichentries(files) | ||
| 254 | if not list: return | ||
| 255 | ok = 1 | ||
| 256 | for e in list: | ||
| 257 | if not e.commitcheck(): | ||
| 258 | ok = 0 | ||
| 259 | if not ok: | ||
| 260 | print "correct above errors first" | ||
| 261 | return | ||
| 262 | if not message: | ||
| 263 | message = raw_input("One-liner: ") | ||
| 264 | committed = [] | ||
| 265 | for e in list: | ||
| 266 | if e.commit(message): | ||
| 267 | committed.append(e.file) | ||
| 268 | self.mailinfo(committed, message) | ||
| 269 | |||
| 270 | def mailinfo(self, files, message = ""): | ||
| 271 | towhom = "sjoerd@cwi.nl, jack@cwi.nl" # XXX | ||
| 272 | mailtext = MAILFORM % (towhom, string.join(files), | ||
| 273 | string.join(files), message) | ||
| 274 | print '-'*70 | ||
| 275 | print mailtext | ||
| 276 | print '-'*70 | ||
| 277 | ok = raw_input("OK to mail to %s? " % towhom) | ||
| 278 | if string.lower(string.strip(ok)) in ('y', 'ye', 'yes'): | ||
| 279 | p = os.popen(SENDMAIL, "w") | ||
| 280 | p.write(mailtext) | ||
| 281 | sts = p.close() | ||
| 282 | if sts: | ||
| 283 | print "Sendmail exit status %s" % str(sts) | ||
| 284 | else: | ||
| 285 | print "Mail sent." | ||
| 286 | else: | ||
| 287 | print "No mail sent." | ||
| 288 | |||
| 289 | def report(self, files): | ||
| 290 | for e in self.whichentries(files): | ||
| 291 | e.report() | ||
| 292 | |||
| 293 | def diff(self, files, opts): | ||
| 294 | for e in self.whichentries(files): | ||
| 295 | e.diff(opts) | ||
| 296 | |||
| 297 | def add(self, files): | ||
| 298 | if not files: | ||
| 299 | raise RuntimeError, "'cvs add' needs at least one file" | ||
| 300 | list = [] | ||
| 301 | for e in self.whichentries(files, 1): | ||
| 302 | e.add() | ||
| 303 | |||
| 304 | def rm(self, files): | ||
| 305 | if not files: | ||
| 306 | raise RuntimeError, "'cvs rm' needs at least one file" | ||
| 307 | raise RuntimeError, "'cvs rm' not yet imlemented" | ||
| 308 | |||
| 309 | def log(self, files, opts): | ||
| 310 | flags = '' | ||
| 311 | for o, a in opts: | ||
| 312 | flags = flags + ' ' + o + a | ||
| 313 | for e in self.whichentries(files): | ||
| 314 | e.log(flags) | ||
| 315 | |||
| 316 | def whichentries(self, files, localfilestoo = 0): | ||
| 317 | if files: | ||
| 318 | list = [] | ||
| 319 | for file in files: | ||
| 320 | if self.entries.has_key(file): | ||
| 321 | e = self.entries[file] | ||
| 322 | else: | ||
| 323 | e = self.FileClass(file) | ||
| 324 | self.entries[file] = e | ||
| 325 | list.append(e) | ||
| 326 | else: | ||
| 327 | list = self.entries.values() | ||
| 328 | for file in self.proxy.listfiles(): | ||
| 329 | if self.entries.has_key(file): | ||
| 330 | continue | ||
| 331 | e = self.FileClass(file) | ||
| 332 | self.entries[file] = e | ||
| 333 | list.append(e) | ||
| 334 | if localfilestoo: | ||
| 335 | for file in os.listdir(os.curdir): | ||
| 336 | if not self.entries.has_key(file) \ | ||
| 337 | and not self.ignored(file): | ||
| 338 | e = self.FileClass(file) | ||
| 339 | self.entries[file] = e | ||
| 340 | list.append(e) | ||
| 341 | list.sort() | ||
| 342 | if self.proxy: | ||
| 343 | for e in list: | ||
| 344 | if e.proxy is None: | ||
| 345 | e.proxy = self.proxy | ||
| 346 | return list | ||
| 347 | |||
| 348 | |||
| 349 | class rcvs(CommandFrameWork): | ||
| 350 | |||
| 351 | GlobalFlags = 'd:h:p:qvL' | ||
| 352 | UsageMessage = \ | ||
| 353 | "usage: rcvs [-d directory] [-h host] [-p port] [-q] [-v] [subcommand arg ...]" | ||
| 354 | PostUsageMessage = \ | ||
| 355 | "If no subcommand is given, the status of all files is listed" | ||
| 356 | |||
| 357 | def __init__(self): | ||
| 358 | """Constructor.""" | ||
| 359 | CommandFrameWork.__init__(self) | ||
| 360 | self.proxy = None | ||
| 361 | self.cvs = RCVS() | ||
| 362 | |||
| 363 | def close(self): | ||
| 364 | if self.proxy: | ||
| 365 | self.proxy._close() | ||
| 366 | self.proxy = None | ||
| 367 | |||
| 368 | def recurse(self): | ||
| 369 | self.close() | ||
| 370 | names = os.listdir(os.curdir) | ||
| 371 | for name in names: | ||
| 372 | if name == os.curdir or name == os.pardir: | ||
| 373 | continue | ||
| 374 | if name == "CVS": | ||
| 375 | continue | ||
| 376 | if not os.path.isdir(name): | ||
| 377 | continue | ||
| 378 | if os.path.islink(name): | ||
| 379 | continue | ||
| 380 | print "--- entering subdirectory", name, "---" | ||
| 381 | os.chdir(name) | ||
| 382 | try: | ||
| 383 | if os.path.isdir("CVS"): | ||
| 384 | self.__class__().run() | ||
| 385 | else: | ||
| 386 | self.recurse() | ||
| 387 | finally: | ||
| 388 | os.chdir(os.pardir) | ||
| 389 | print "--- left subdirectory", name, "---" | ||
| 390 | |||
| 391 | def options(self, opts): | ||
| 392 | self.opts = opts | ||
| 393 | |||
| 394 | def ready(self): | ||
| 395 | import rcsclient | ||
| 396 | self.proxy = rcsclient.openrcsclient(self.opts) | ||
| 397 | self.cvs.setproxy(self.proxy) | ||
| 398 | self.cvs.getentries() | ||
| 399 | |||
| 400 | def default(self): | ||
| 401 | self.cvs.report([]) | ||
| 402 | |||
| 403 | def do_report(self, opts, files): | ||
| 404 | self.cvs.report(files) | ||
| 405 | |||
| 406 | def do_update(self, opts, files): | ||
| 407 | """update [-l] [-R] [file] ...""" | ||
| 408 | local = DEF_LOCAL | ||
| 409 | for o, a in opts: | ||
| 410 | if o == '-l': local = 1 | ||
| 411 | if o == '-R': local = 0 | ||
| 412 | self.cvs.update(files) | ||
| 413 | self.cvs.putentries() | ||
| 414 | if not local and not files: | ||
| 415 | self.recurse() | ||
| 416 | flags_update = '-lR' | ||
| 417 | do_up = do_update | ||
| 418 | flags_up = flags_update | ||
| 419 | |||
| 420 | def do_commit(self, opts, files): | ||
| 421 | """commit [-m message] [file] ...""" | ||
| 422 | message = "" | ||
| 423 | for o, a in opts: | ||
| 424 | if o == '-m': message = a | ||
| 425 | self.cvs.commit(files, message) | ||
| 426 | self.cvs.putentries() | ||
| 427 | flags_commit = 'm:' | ||
| 428 | do_com = do_commit | ||
| 429 | flags_com = flags_commit | ||
| 430 | |||
| 431 | def do_diff(self, opts, files): | ||
| 432 | """diff [difflags] [file] ...""" | ||
| 433 | self.cvs.diff(files, opts) | ||
| 434 | flags_diff = 'cbitwcefhnlr:sD:S:' | ||
| 435 | do_dif = do_diff | ||
| 436 | flags_dif = flags_diff | ||
| 437 | |||
| 438 | def do_add(self, opts, files): | ||
| 439 | """add file ...""" | ||
| 440 | if not files: | ||
| 441 | print "'rcvs add' requires at least one file" | ||
| 442 | return | ||
| 443 | self.cvs.add(files) | ||
| 444 | self.cvs.putentries() | ||
| 445 | |||
| 446 | def do_remove(self, opts, files): | ||
| 447 | """remove file ...""" | ||
| 448 | if not files: | ||
| 449 | print "'rcvs remove' requires at least one file" | ||
| 450 | return | ||
| 451 | self.cvs.remove(files) | ||
| 452 | self.cvs.putentries() | ||
| 453 | do_rm = do_remove | ||
| 454 | |||
| 455 | def do_log(self, opts, files): | ||
| 456 | """log [rlog-options] [file] ...""" | ||
| 457 | self.cvs.log(files, opts) | ||
| 458 | flags_log = 'bhLNRtd:s:V:r:' | ||
| 459 | |||
| 460 | |||
| 461 | def remove(fn): | ||
| 462 | try: | ||
| 463 | os.unlink(fn) | ||
| 464 | except os.error: | ||
| 465 | pass | ||
| 466 | |||
| 467 | |||
| 468 | def main(): | ||
| 469 | r = rcvs() | ||
| 470 | try: | ||
| 471 | r.run() | ||
| 472 | finally: | ||
| 473 | r.close() | ||
| 474 | |||
| 475 | |||
| 476 | if __name__ == "__main__": | ||
| 477 | main() | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | import addpack | ||
| 4 | addpack.addpack('/home/guido/src/python/Demo/pdist') | ||
| 5 | |||
| 6 | import rrcs | ||
| 7 | |||
| 8 | rrcs.main() | ||
| @@ -0,0 +1,160 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | "Remote RCS -- command line interface" | ||
| 4 | |||
| 5 | import sys | ||
| 6 | import os | ||
| 7 | import getopt | ||
| 8 | import string | ||
| 9 | import md5 | ||
| 10 | import tempfile | ||
| 11 | from rcsclient import openrcsclient | ||
| 12 | |||
| 13 | def main(): | ||
| 14 | sys.stdout = sys.stderr | ||
| 15 | try: | ||
| 16 | opts, rest = getopt.getopt(sys.argv[1:], 'h:p:d:qvL') | ||
| 17 | if not rest: | ||
| 18 | cmd = 'head' | ||
| 19 | else: | ||
| 20 | cmd, rest = rest[0], rest[1:] | ||
| 21 | if not commands.has_key(cmd): | ||
| 22 | raise getopt.error, "unknown command" | ||
| 23 | coptset, func = commands[cmd] | ||
| 24 | copts, files = getopt.getopt(rest, coptset) | ||
| 25 | except getopt.error, msg: | ||
| 26 | print msg | ||
| 27 | print "usage: rrcs [options] command [options] [file] ..." | ||
| 28 | print "where command can be:" | ||
| 29 | print " ci|put # checkin the given files" | ||
| 30 | print " co|get # checkout" | ||
| 31 | print " info # print header info" | ||
| 32 | print " head # print revision of head branch" | ||
| 33 | print " list # list filename if valid" | ||
| 34 | print " log # print full log" | ||
| 35 | print " diff # diff rcs file and work file" | ||
| 36 | print "if no files are given, all remote rcs files are assumed" | ||
| 37 | sys.exit(2) | ||
| 38 | x = openrcsclient(opts) | ||
| 39 | if not files: | ||
| 40 | files = x.listfiles() | ||
| 41 | for fn in files: | ||
| 42 | try: | ||
| 43 | func(x, copts, fn) | ||
| 44 | except (IOError, os.error), msg: | ||
| 45 | print "%s: %s" % (fn, msg) | ||
| 46 | |||
| 47 | def checkin(x, copts, fn): | ||
| 48 | f = open(fn) | ||
| 49 | data = f.read() | ||
| 50 | f.close() | ||
| 51 | new = not x.isvalid(fn) | ||
| 52 | if not new and same(x, copts, fn, data): | ||
| 53 | print "%s: unchanged since last checkin" % fn | ||
| 54 | return | ||
| 55 | print "Checking in", fn, "..." | ||
| 56 | message = asklogmessage(new) | ||
| 57 | messages = x.put(fn, data, message) | ||
| 58 | if messages: | ||
| 59 | print messages | ||
| 60 | |||
| 61 | def checkout(x, copts, fn): | ||
| 62 | data = x.get(fn) | ||
| 63 | f = open(fn, 'w') | ||
| 64 | f.write(data) | ||
| 65 | f.close() | ||
| 66 | |||
| 67 | def lock(x, copts, fn): | ||
| 68 | x.lock(fn) | ||
| 69 | |||
| 70 | def unlock(x, copts, fn): | ||
| 71 | x.unlock(fn) | ||
| 72 | |||
| 73 | def info(x, copts, fn): | ||
| 74 | dict = x.info(fn) | ||
| 75 | keys = dict.keys() | ||
| 76 | keys.sort() | ||
| 77 | for key in keys: | ||
| 78 | print key + ':', dict[key] | ||
| 79 | print '='*70 | ||
| 80 | |||
| 81 | def head(x, copts, fn): | ||
| 82 | head = x.head(fn) | ||
| 83 | print fn, head | ||
| 84 | |||
| 85 | def list(x, copts, fn): | ||
| 86 | if x.isvalid(fn): | ||
| 87 | print fn | ||
| 88 | |||
| 89 | def log(x, copts, fn): | ||
| 90 | flags = '' | ||
| 91 | for o, a in copts: | ||
| 92 | flags = flags + ' ' + o + a | ||
| 93 | flags = flags[1:] | ||
| 94 | messages = x.log(fn, flags) | ||
| 95 | print messages | ||
| 96 | |||
| 97 | def diff(x, copts, fn): | ||
| 98 | if same(x, copts, fn): | ||
| 99 | return | ||
| 100 | flags = '' | ||
| 101 | for o, a in copts: | ||
| 102 | flags = flags + ' ' + o + a | ||
| 103 | flags = flags[1:] | ||
| 104 | data = x.get(fn) | ||
| 105 | tf = tempfile.NamedTemporaryFile() | ||
| 106 | tf.write(data) | ||
| 107 | tf.flush() | ||
| 108 | print 'diff %s -r%s %s' % (flags, x.head(fn), fn) | ||
| 109 | sts = os.system('diff %s %s %s' % (flags, tf.name, fn)) | ||
| 110 | if sts: | ||
| 111 | print '='*70 | ||
| 112 | |||
| 113 | def same(x, copts, fn, data = None): | ||
| 114 | if data is None: | ||
| 115 | f = open(fn) | ||
| 116 | data = f.read() | ||
| 117 | f.close() | ||
| 118 | lsum = md5.new(data).digest() | ||
| 119 | rsum = x.sum(fn) | ||
| 120 | return lsum == rsum | ||
| 121 | |||
| 122 | def asklogmessage(new): | ||
| 123 | if new: | ||
| 124 | print "enter description,", | ||
| 125 | else: | ||
| 126 | print "enter log message,", | ||
| 127 | print "terminate with single '.' or end of file:" | ||
| 128 | if new: | ||
| 129 | print "NOTE: This is NOT the log message!" | ||
| 130 | message = "" | ||
| 131 | while 1: | ||
| 132 | sys.stderr.write(">> ") | ||
| 133 | sys.stderr.flush() | ||
| 134 | line = sys.stdin.readline() | ||
| 135 | if not line or line == '.\n': break | ||
| 136 | message = message + line | ||
| 137 | return message | ||
| 138 | |||
| 139 | def remove(fn): | ||
| 140 | try: | ||
| 141 | os.unlink(fn) | ||
| 142 | except os.error: | ||
| 143 | pass | ||
| 144 | |||
| 145 | commands = { | ||
| 146 | 'ci': ('', checkin), | ||
| 147 | 'put': ('', checkin), | ||
| 148 | 'co': ('', checkout), | ||
| 149 | 'get': ('', checkout), | ||
| 150 | 'info': ('', info), | ||
| 151 | 'head': ('', head), | ||
| 152 | 'list': ('', list), | ||
| 153 | 'lock': ('', lock), | ||
| 154 | 'unlock': ('', unlock), | ||
| 155 | 'log': ('bhLRtd:l:r:s:w:V:', log), | ||
| 156 | 'diff': ('c', diff), | ||
| 157 | } | ||
| 158 | |||
| 159 | if __name__ == '__main__': | ||
| 160 | main() | ||
| @@ -0,0 +1,33 @@ | |||
| 1 | class Security: | ||
| 2 | |||
| 3 | def __init__(self): | ||
| 4 | import os | ||
| 5 | env = os.environ | ||
| 6 | if env.has_key('PYTHON_KEYFILE'): | ||
| 7 | keyfile = env['PYTHON_KEYFILE'] | ||
| 8 | else: | ||
| 9 | keyfile = '.python_keyfile' | ||
| 10 | if env.has_key('HOME'): | ||
| 11 | keyfile = os.path.join(env['HOME'], keyfile) | ||
| 12 | if not os.path.exists(keyfile): | ||
| 13 | import sys | ||
| 14 | for dir in sys.path: | ||
| 15 | kf = os.path.join(dir, keyfile) | ||
| 16 | if os.path.exists(kf): | ||
| 17 | keyfile = kf | ||
| 18 | break | ||
| 19 | try: | ||
| 20 | self._key = eval(open(keyfile).readline()) | ||
| 21 | except IOError: | ||
| 22 | raise IOError, "python keyfile %s: cannot open" % keyfile | ||
| 23 | |||
| 24 | def _generate_challenge(self): | ||
| 25 | import random | ||
| 26 | return random.randint(100, 100000) | ||
| 27 | |||
| 28 | def _compare_challenge_response(self, challenge, response): | ||
| 29 | return self._encode_challenge(challenge) == response | ||
| 30 | |||
| 31 | def _encode_challenge(self, challenge): | ||
| 32 | p, m = self._key | ||
| 33 | return pow(long(challenge), p, m) | ||
| @@ -0,0 +1,145 @@ | |||
| 1 | """RPC Server module.""" | ||
| 2 | |||
| 3 | import sys | ||
| 4 | import socket | ||
| 5 | import pickle | ||
| 6 | from fnmatch import fnmatch | ||
| 7 | from repr import repr | ||
| 8 | |||
| 9 | |||
| 10 | # Default verbosity (0 = silent, 1 = print connections, 2 = print requests too) | ||
| 11 | VERBOSE = 1 | ||
| 12 | |||
| 13 | |||
| 14 | class Server: | ||
| 15 | |||
| 16 | """RPC Server class. Derive a class to implement a particular service.""" | ||
| 17 | |||
| 18 | def __init__(self, address, verbose = VERBOSE): | ||
| 19 | if type(address) == type(0): | ||
| 20 | address = ('', address) | ||
| 21 | self._address = address | ||
| 22 | self._verbose = verbose | ||
| 23 | self._socket = None | ||
| 24 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 25 | self._socket.bind(address) | ||
| 26 | self._socket.listen(1) | ||
| 27 | self._listening = 1 | ||
| 28 | |||
| 29 | def _setverbose(self, verbose): | ||
| 30 | self._verbose = verbose | ||
| 31 | |||
| 32 | def __del__(self): | ||
| 33 | self._close() | ||
| 34 | |||
| 35 | def _close(self): | ||
| 36 | self._listening = 0 | ||
| 37 | if self._socket: | ||
| 38 | self._socket.close() | ||
| 39 | self._socket = None | ||
| 40 | |||
| 41 | def _serverloop(self): | ||
| 42 | while self._listening: | ||
| 43 | self._serve() | ||
| 44 | |||
| 45 | def _serve(self): | ||
| 46 | if self._verbose: print "Wait for connection ..." | ||
| 47 | conn, address = self._socket.accept() | ||
| 48 | if self._verbose: print "Accepted connection from %s" % repr(address) | ||
| 49 | if not self._verify(conn, address): | ||
| 50 | print "*** Connection from %s refused" % repr(address) | ||
| 51 | conn.close() | ||
| 52 | return | ||
| 53 | rf = conn.makefile('r') | ||
| 54 | wf = conn.makefile('w') | ||
| 55 | ok = 1 | ||
| 56 | while ok: | ||
| 57 | wf.flush() | ||
| 58 | if self._verbose > 1: print "Wait for next request ..." | ||
| 59 | ok = self._dorequest(rf, wf) | ||
| 60 | |||
| 61 | _valid = ['192.16.201.*', '192.16.197.*', '132.151.1.*', '129.6.64.*'] | ||
| 62 | |||
| 63 | def _verify(self, conn, address): | ||
| 64 | host, port = address | ||
| 65 | for pat in self._valid: | ||
| 66 | if fnmatch(host, pat): return 1 | ||
| 67 | return 0 | ||
| 68 | |||
| 69 | def _dorequest(self, rf, wf): | ||
| 70 | rp = pickle.Unpickler(rf) | ||
| 71 | try: | ||
| 72 | request = rp.load() | ||
| 73 | except EOFError: | ||
| 74 | return 0 | ||
| 75 | if self._verbose > 1: print "Got request: %s" % repr(request) | ||
| 76 | try: | ||
| 77 | methodname, args, id = request | ||
| 78 | if '.' in methodname: | ||
| 79 | reply = (None, self._special(methodname, args), id) | ||
| 80 | elif methodname[0] == '_': | ||
| 81 | raise NameError, "illegal method name %s" % repr(methodname) | ||
| 82 | else: | ||
| 83 | method = getattr(self, methodname) | ||
| 84 | reply = (None, apply(method, args), id) | ||
| 85 | except: | ||
| 86 | reply = (sys.exc_type, sys.exc_value, id) | ||
| 87 | if id < 0 and reply[:2] == (None, None): | ||
| 88 | if self._verbose > 1: print "Suppress reply" | ||
| 89 | return 1 | ||
| 90 | if self._verbose > 1: print "Send reply: %s" % repr(reply) | ||
| 91 | wp = pickle.Pickler(wf) | ||
| 92 | wp.dump(reply) | ||
| 93 | return 1 | ||
| 94 | |||
| 95 | def _special(self, methodname, args): | ||
| 96 | if methodname == '.methods': | ||
| 97 | if not hasattr(self, '_methods'): | ||
| 98 | self._methods = tuple(self._listmethods()) | ||
| 99 | return self._methods | ||
| 100 | raise NameError, "unrecognized special method name %s" % repr(methodname) | ||
| 101 | |||
| 102 | def _listmethods(self, cl=None): | ||
| 103 | if not cl: cl = self.__class__ | ||
| 104 | names = cl.__dict__.keys() | ||
| 105 | names = filter(lambda x: x[0] != '_', names) | ||
| 106 | names.sort() | ||
| 107 | for base in cl.__bases__: | ||
| 108 | basenames = self._listmethods(base) | ||
| 109 | basenames = filter(lambda x, names=names: x not in names, basenames) | ||
| 110 | names[len(names):] = basenames | ||
| 111 | return names | ||
| 112 | |||
| 113 | |||
| 114 | from security import Security | ||
| 115 | |||
| 116 | |||
| 117 | class SecureServer(Server, Security): | ||
| 118 | |||
| 119 | def __init__(self, *args): | ||
| 120 | apply(Server.__init__, (self,) + args) | ||
| 121 | Security.__init__(self) | ||
| 122 | |||
| 123 | def _verify(self, conn, address): | ||
| 124 | import string | ||
| 125 | challenge = self._generate_challenge() | ||
| 126 | conn.send("%d\n" % challenge) | ||
| 127 | response = "" | ||
| 128 | while "\n" not in response and len(response) < 100: | ||
| 129 | data = conn.recv(100) | ||
| 130 | if not data: | ||
| 131 | break | ||
| 132 | response = response + data | ||
| 133 | try: | ||
| 134 | response = string.atol(string.strip(response)) | ||
| 135 | except string.atol_error: | ||
| 136 | if self._verbose > 0: | ||
| 137 | print "Invalid response syntax", repr(response) | ||
| 138 | return 0 | ||
| 139 | if not self._compare_challenge_response(challenge, response): | ||
| 140 | if self._verbose > 0: | ||
| 141 | print "Invalid response value", repr(response) | ||
| 142 | return 0 | ||
| 143 | if self._verbose > 1: | ||
| 144 | print "Response matches challenge. Go ahead!" | ||
| 145 | return 1 | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | import time | ||
| 2 | import FSProxy | ||
| 3 | |||
| 4 | def main(): | ||
| 5 | t1 = time.time() | ||
| 6 | #proxy = FSProxy.FSProxyClient(('voorn.cwi.nl', 4127)) | ||
| 7 | proxy = FSProxy.FSProxyLocal() | ||
| 8 | sumtree(proxy) | ||
| 9 | proxy._close() | ||
| 10 | t2 = time.time() | ||
| 11 | print t2-t1, "seconds" | ||
| 12 | raw_input("[Return to exit] ") | ||
| 13 | |||
| 14 | def sumtree(proxy): | ||
| 15 | print "PWD =", proxy.pwd() | ||
| 16 | files = proxy.listfiles() | ||
| 17 | proxy.infolist(files) | ||
| 18 | subdirs = proxy.listsubdirs() | ||
| 19 | for name in subdirs: | ||
| 20 | proxy.cd(name) | ||
| 21 | sumtree(proxy) | ||
| 22 | proxy.back() | ||
| 23 | |||
| 24 | main() | ||
| @@ -0,0 +1,57 @@ | |||
| 1 | # Makefile for 'pysvr' application embedding Python. | ||
| 2 | # Tailored for Python 1.5a3 or later. | ||
| 3 | # Some details are specific for Solaris or CNRI. | ||
| 4 | # Also see ## comments for tailoring. | ||
| 5 | |||
| 6 | # Which C compiler | ||
| 7 | CC=gcc | ||
| 8 | ##PURIFY=/usr/local/pure/purify | ||
| 9 | LINKCC=$(PURIFY) $(CC) | ||
| 10 | |||
| 11 | # Optimization preferences | ||
| 12 | OPT=-g | ||
| 13 | |||
| 14 | # Which Python version we're using | ||
| 15 | VER=2.2 | ||
| 16 | |||
| 17 | # Expressions using the above definitions | ||
| 18 | PYVER=python$(VER) | ||
| 19 | |||
| 20 | # Use these defs when compiling against installed Python | ||
| 21 | ##INST=/usr/local | ||
| 22 | ##PYC=$(INST)/lib/$(PYVER)/config | ||
| 23 | ##PYINCL=-I$(INST)/include/$(PYVER) -I$(PYC) | ||
| 24 | ##PYLIBS=$(PYC)/lib$(PYVER).a | ||
| 25 | |||
| 26 | # Use these defs when compiling against built Python | ||
| 27 | PLAT=linux | ||
| 28 | PYINCL=-I../../Include -I../../$(PLAT) | ||
| 29 | PYLIBS=../../$(PLAT)/lib$(PYVER).a | ||
| 30 | |||
| 31 | # Libraries to link with -- very installation dependent | ||
| 32 | # (See LIBS= in Modules/Makefile in build tree) | ||
| 33 | RLLIBS=-lreadline -ltermcap | ||
| 34 | OTHERLIBS=-lnsl -lpthread -ldl -lm -ldb -lutil | ||
| 35 | |||
| 36 | # Compilation and link flags -- no need to change normally | ||
| 37 | CFLAGS=$(OPT) | ||
| 38 | CPPFLAGS=$(PYINCL) | ||
| 39 | LIBS=$(PYLIBS) $(RLLIBS) $(OTHERLIBS) | ||
| 40 | |||
| 41 | # Default port for the pysvr application | ||
| 42 | PORT=4000 | ||
| 43 | |||
| 44 | # Default target | ||
| 45 | all: pysvr | ||
| 46 | |||
| 47 | # Target to build pysvr | ||
| 48 | pysvr: pysvr.o $(PYOBJS) $(PYLIBS) | ||
| 49 | $(LINKCC) pysvr.o $(LIBS) -o pysvr | ||
| 50 | |||
| 51 | # Target to build and run pysvr | ||
| 52 | run: pysvr | ||
| 53 | pysvr $(PORT) | ||
| 54 | |||
| 55 | # Target to clean up the directory | ||
| 56 | clean: | ||
| 57 | -rm -f pysvr *.o *~ core | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | This is an example of a multi-threaded C application embedding a | ||
| 2 | Python interpreter. | ||
| 3 | |||
| 4 | The particular application is a multi-threaded telnet-like server that | ||
| 5 | provides you with a Python prompt (instead of a shell prompt). | ||
| 6 | |||
| 7 | The file pysvr.py is a prototype in Python. | ||
| 8 | |||
| 9 | THIS APPLICATION IS NOT SECURE -- ONLY USE IT FOR TESTING! | ||
| @@ -0,0 +1,370 @@ | |||
| 1 | /* A multi-threaded telnet-like server that gives a Python prompt. | ||
| 2 | |||
| 3 | Usage: pysvr [port] | ||
| 4 | |||
| 5 | For security reasons, it only accepts requests from the current host. | ||
| 6 | This can still be insecure, but restricts violations from people who | ||
| 7 | can log in on your machine. Use with caution! | ||
| 8 | |||
| 9 | */ | ||
| 10 | |||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <string.h> | ||
| 14 | #include <ctype.h> | ||
| 15 | #include <errno.h> | ||
| 16 | |||
| 17 | #include <sys/types.h> | ||
| 18 | #include <sys/socket.h> | ||
| 19 | #include <netinet/in.h> | ||
| 20 | |||
| 21 | #include <pthread.h> | ||
| 22 | #include <getopt.h> | ||
| 23 | |||
| 24 | /* XXX Umpfh. | ||
| 25 | Python.h defines a typedef destructor, which conflicts with pthread.h. | ||
| 26 | So Python.h must be included after pthread.h. */ | ||
| 27 | |||
| 28 | #include "Python.h" | ||
| 29 | |||
| 30 | extern int Py_VerboseFlag; | ||
| 31 | |||
| 32 | #ifndef PORT | ||
| 33 | #define PORT 4000 | ||
| 34 | #endif | ||
| 35 | |||
| 36 | struct workorder { | ||
| 37 | int conn; | ||
| 38 | struct sockaddr_in addr; | ||
| 39 | }; | ||
| 40 | |||
| 41 | /* Forward */ | ||
| 42 | static void init_python(void); | ||
| 43 | static void usage(void); | ||
| 44 | static void oprogname(void); | ||
| 45 | static void main_thread(int); | ||
| 46 | static void create_thread(int, struct sockaddr_in *); | ||
| 47 | static void *service_thread(struct workorder *); | ||
| 48 | static void run_interpreter(FILE *, FILE *); | ||
| 49 | static int run_command(char *, PyObject *); | ||
| 50 | static void ps(void); | ||
| 51 | |||
| 52 | static char *progname = "pysvr"; | ||
| 53 | |||
| 54 | static PyThreadState *gtstate; | ||
| 55 | |||
| 56 | main(int argc, char **argv) | ||
| 57 | { | ||
| 58 | int port = PORT; | ||
| 59 | int c; | ||
| 60 | |||
| 61 | if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0') | ||
| 62 | progname = argv[0]; | ||
| 63 | |||
| 64 | while ((c = getopt(argc, argv, "v")) != EOF) { | ||
| 65 | switch (c) { | ||
| 66 | case 'v': | ||
| 67 | Py_VerboseFlag++; | ||
| 68 | break; | ||
| 69 | default: | ||
| 70 | usage(); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | if (optind < argc) { | ||
| 75 | if (optind+1 < argc) { | ||
| 76 | oprogname(); | ||
| 77 | fprintf(stderr, "too many arguments\n"); | ||
| 78 | usage(); | ||
| 79 | } | ||
| 80 | port = atoi(argv[optind]); | ||
| 81 | if (port <= 0) { | ||
| 82 | fprintf(stderr, "bad port (%s)\n", argv[optind]); | ||
| 83 | usage(); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | main_thread(port); | ||
| 88 | |||
| 89 | fprintf(stderr, "Bye.\n"); | ||
| 90 | |||
| 91 | exit(0); | ||
| 92 | } | ||
| 93 | |||
| 94 | static char usage_line[] = "usage: %s [port]\n"; | ||
| 95 | |||
| 96 | static void | ||
| 97 | usage(void) | ||
| 98 | { | ||
| 99 | fprintf(stderr, usage_line, progname); | ||
| 100 | exit(2); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void | ||
| 104 | main_thread(int port) | ||
| 105 | { | ||
| 106 | int sock, conn, size, i; | ||
| 107 | struct sockaddr_in addr, clientaddr; | ||
| 108 | |||
| 109 | sock = socket(PF_INET, SOCK_STREAM, 0); | ||
| 110 | if (sock < 0) { | ||
| 111 | oprogname(); | ||
| 112 | perror("can't create socket"); | ||
| 113 | exit(1); | ||
| 114 | } | ||
| 115 | |||
| 116 | #ifdef SO_REUSEADDR | ||
| 117 | i = 1; | ||
| 118 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i); | ||
| 119 | #endif | ||
| 120 | |||
| 121 | memset((char *)&addr, '\0', sizeof addr); | ||
| 122 | addr.sin_family = AF_INET; | ||
| 123 | addr.sin_port = htons(port); | ||
| 124 | addr.sin_addr.s_addr = 0L; | ||
| 125 | if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) { | ||
| 126 | oprogname(); | ||
| 127 | perror("can't bind socket to address"); | ||
| 128 | exit(1); | ||
| 129 | } | ||
| 130 | |||
| 131 | if (listen(sock, 5) < 0) { | ||
| 132 | oprogname(); | ||
| 133 | perror("can't listen on socket"); | ||
| 134 | exit(1); | ||
| 135 | } | ||
| 136 | |||
| 137 | fprintf(stderr, "Listening on port %d...\n", port); | ||
| 138 | |||
| 139 | for (i = 0; ; i++) { | ||
| 140 | size = sizeof clientaddr; | ||
| 141 | memset((char *) &clientaddr, '\0', size); | ||
| 142 | conn = accept(sock, (struct sockaddr *) &clientaddr, &size); | ||
| 143 | if (conn < 0) { | ||
| 144 | oprogname(); | ||
| 145 | perror("can't accept connection from socket"); | ||
| 146 | exit(1); | ||
| 147 | } | ||
| 148 | |||
| 149 | size = sizeof addr; | ||
| 150 | memset((char *) &addr, '\0', size); | ||
| 151 | if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) { | ||
| 152 | oprogname(); | ||
| 153 | perror("can't get socket name of connection"); | ||
| 154 | exit(1); | ||
| 155 | } | ||
| 156 | if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) { | ||
| 157 | oprogname(); | ||
| 158 | perror("connection from non-local host refused"); | ||
| 159 | fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n", | ||
| 160 | ntohl(addr.sin_addr.s_addr), | ||
| 161 | ntohl(clientaddr.sin_addr.s_addr)); | ||
| 162 | close(conn); | ||
| 163 | continue; | ||
| 164 | } | ||
| 165 | if (i == 4) { | ||
| 166 | close(conn); | ||
| 167 | break; | ||
| 168 | } | ||
| 169 | create_thread(conn, &clientaddr); | ||
| 170 | } | ||
| 171 | |||
| 172 | close(sock); | ||
| 173 | |||
| 174 | if (gtstate) { | ||
| 175 | PyEval_AcquireThread(gtstate); | ||
| 176 | gtstate = NULL; | ||
| 177 | Py_Finalize(); | ||
| 178 | /* And a second time, just because we can. */ | ||
| 179 | Py_Finalize(); /* This should be harmless. */ | ||
| 180 | } | ||
| 181 | exit(0); | ||
| 182 | } | ||
| 183 | |||
| 184 | static void | ||
| 185 | create_thread(int conn, struct sockaddr_in *addr) | ||
| 186 | { | ||
| 187 | struct workorder *work; | ||
| 188 | pthread_t tdata; | ||
| 189 | |||
| 190 | work = malloc(sizeof(struct workorder)); | ||
| 191 | if (work == NULL) { | ||
| 192 | oprogname(); | ||
| 193 | fprintf(stderr, "out of memory for thread.\n"); | ||
| 194 | close(conn); | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | work->conn = conn; | ||
| 198 | work->addr = *addr; | ||
| 199 | |||
| 200 | init_python(); | ||
| 201 | |||
| 202 | if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) { | ||
| 203 | oprogname(); | ||
| 204 | perror("can't create new thread"); | ||
| 205 | close(conn); | ||
| 206 | return; | ||
| 207 | } | ||
| 208 | |||
| 209 | if (pthread_detach(tdata) < 0) { | ||
| 210 | oprogname(); | ||
| 211 | perror("can't detach from thread"); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | static PyThreadState *the_tstate; | ||
| 216 | static PyInterpreterState *the_interp; | ||
| 217 | static PyObject *the_builtins; | ||
| 218 | |||
| 219 | static void | ||
| 220 | init_python(void) | ||
| 221 | { | ||
| 222 | if (gtstate) | ||
| 223 | return; | ||
| 224 | Py_Initialize(); /* Initialize the interpreter */ | ||
| 225 | PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */ | ||
| 226 | gtstate = PyEval_SaveThread(); /* Release the thread state */ | ||
| 227 | } | ||
| 228 | |||
| 229 | static void * | ||
| 230 | service_thread(struct workorder *work) | ||
| 231 | { | ||
| 232 | FILE *input, *output; | ||
| 233 | |||
| 234 | fprintf(stderr, "Start thread for connection %d.\n", work->conn); | ||
| 235 | |||
| 236 | ps(); | ||
| 237 | |||
| 238 | input = fdopen(work->conn, "r"); | ||
| 239 | if (input == NULL) { | ||
| 240 | oprogname(); | ||
| 241 | perror("can't create input stream"); | ||
| 242 | goto done; | ||
| 243 | } | ||
| 244 | |||
| 245 | output = fdopen(work->conn, "w"); | ||
| 246 | if (output == NULL) { | ||
| 247 | oprogname(); | ||
| 248 | perror("can't create output stream"); | ||
| 249 | fclose(input); | ||
| 250 | goto done; | ||
| 251 | } | ||
| 252 | |||
| 253 | setvbuf(input, NULL, _IONBF, 0); | ||
| 254 | setvbuf(output, NULL, _IONBF, 0); | ||
| 255 | |||
| 256 | run_interpreter(input, output); | ||
| 257 | |||
| 258 | fclose(input); | ||
| 259 | fclose(output); | ||
| 260 | |||
| 261 | done: | ||
| 262 | fprintf(stderr, "End thread for connection %d.\n", work->conn); | ||
| 263 | close(work->conn); | ||
| 264 | free(work); | ||
| 265 | } | ||
| 266 | |||
| 267 | static void | ||
| 268 | oprogname(void) | ||
| 269 | { | ||
| 270 | int save = errno; | ||
| 271 | fprintf(stderr, "%s: ", progname); | ||
| 272 | errno = save; | ||
| 273 | } | ||
| 274 | |||
| 275 | static void | ||
| 276 | run_interpreter(FILE *input, FILE *output) | ||
| 277 | { | ||
| 278 | PyThreadState *tstate; | ||
| 279 | PyObject *new_stdin, *new_stdout; | ||
| 280 | PyObject *mainmod, *globals; | ||
| 281 | char buffer[1000]; | ||
| 282 | char *p, *q; | ||
| 283 | int n, end; | ||
| 284 | |||
| 285 | PyEval_AcquireLock(); | ||
| 286 | tstate = Py_NewInterpreter(); | ||
| 287 | if (tstate == NULL) { | ||
| 288 | fprintf(output, "Sorry -- can't create an interpreter\n"); | ||
| 289 | return; | ||
| 290 | } | ||
| 291 | |||
| 292 | mainmod = PyImport_AddModule("__main__"); | ||
| 293 | globals = PyModule_GetDict(mainmod); | ||
| 294 | Py_INCREF(globals); | ||
| 295 | |||
| 296 | new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL); | ||
| 297 | new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL); | ||
| 298 | |||
| 299 | PySys_SetObject("stdin", new_stdin); | ||
| 300 | PySys_SetObject("stdout", new_stdout); | ||
| 301 | PySys_SetObject("stderr", new_stdout); | ||
| 302 | |||
| 303 | for (n = 1; !PyErr_Occurred(); n++) { | ||
| 304 | Py_BEGIN_ALLOW_THREADS | ||
| 305 | fprintf(output, "%d> ", n); | ||
| 306 | p = fgets(buffer, sizeof buffer, input); | ||
| 307 | Py_END_ALLOW_THREADS | ||
| 308 | |||
| 309 | if (p == NULL) | ||
| 310 | break; | ||
| 311 | if (p[0] == '\377' && p[1] == '\354') | ||
| 312 | break; | ||
| 313 | |||
| 314 | q = strrchr(p, '\r'); | ||
| 315 | if (q && q[1] == '\n' && q[2] == '\0') { | ||
| 316 | *q++ = '\n'; | ||
| 317 | *q++ = '\0'; | ||
| 318 | } | ||
| 319 | |||
| 320 | while (*p && isspace(*p)) | ||
| 321 | p++; | ||
| 322 | if (p[0] == '#' || p[0] == '\0') | ||
| 323 | continue; | ||
| 324 | |||
| 325 | end = run_command(buffer, globals); | ||
| 326 | if (end < 0) | ||
| 327 | PyErr_Print(); | ||
| 328 | |||
| 329 | if (end) | ||
| 330 | break; | ||
| 331 | } | ||
| 332 | |||
| 333 | Py_XDECREF(globals); | ||
| 334 | Py_XDECREF(new_stdin); | ||
| 335 | Py_XDECREF(new_stdout); | ||
| 336 | |||
| 337 | Py_EndInterpreter(tstate); | ||
| 338 | PyEval_ReleaseLock(); | ||
| 339 | |||
| 340 | fprintf(output, "Goodbye!\n"); | ||
| 341 | } | ||
| 342 | |||
| 343 | static int | ||
| 344 | run_command(char *buffer, PyObject *globals) | ||
| 345 | { | ||
| 346 | PyObject *m, *d, *v; | ||
| 347 | fprintf(stderr, "run_command: %s", buffer); | ||
| 348 | if (strchr(buffer, '\n') == NULL) | ||
| 349 | fprintf(stderr, "\n"); | ||
| 350 | v = PyRun_String(buffer, Py_single_input, globals, globals); | ||
| 351 | if (v == NULL) { | ||
| 352 | if (PyErr_Occurred() == PyExc_SystemExit) { | ||
| 353 | PyErr_Clear(); | ||
| 354 | return 1; | ||
| 355 | } | ||
| 356 | PyErr_Print(); | ||
| 357 | return 0; | ||
| 358 | } | ||
| 359 | Py_DECREF(v); | ||
| 360 | return 0; | ||
| 361 | } | ||
| 362 | |||
| 363 | static void | ||
| 364 | ps(void) | ||
| 365 | { | ||
| 366 | char buffer[100]; | ||
| 367 | PyOS_snprintf(buffer, sizeof(buffer), | ||
| 368 | "ps -l -p %d </dev/null | sed 1d\n", getpid()); | ||
| 369 | system(buffer); | ||
| 370 | } | ||
| @@ -0,0 +1,124 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """A multi-threaded telnet-like server that gives a Python prompt. | ||
| 4 | |||
| 5 | This is really a prototype for the same thing in C. | ||
| 6 | |||
| 7 | Usage: pysvr.py [port] | ||
| 8 | |||
| 9 | For security reasons, it only accepts requests from the current host. | ||
| 10 | This can still be insecure, but restricts violations from people who | ||
| 11 | can log in on your machine. Use with caution! | ||
| 12 | |||
| 13 | """ | ||
| 14 | |||
| 15 | import sys, os, string, getopt, thread, socket, traceback | ||
| 16 | |||
| 17 | PORT = 4000 # Default port | ||
| 18 | |||
| 19 | def main(): | ||
| 20 | try: | ||
| 21 | opts, args = getopt.getopt(sys.argv[1:], "") | ||
| 22 | if len(args) > 1: | ||
| 23 | raise getopt.error, "Too many arguments." | ||
| 24 | except getopt.error, msg: | ||
| 25 | usage(msg) | ||
| 26 | for o, a in opts: | ||
| 27 | pass | ||
| 28 | if args: | ||
| 29 | try: | ||
| 30 | port = string.atoi(args[0]) | ||
| 31 | except ValueError, msg: | ||
| 32 | usage(msg) | ||
| 33 | else: | ||
| 34 | port = PORT | ||
| 35 | main_thread(port) | ||
| 36 | |||
| 37 | def usage(msg=None): | ||
| 38 | sys.stdout = sys.stderr | ||
| 39 | if msg: | ||
| 40 | print msg | ||
| 41 | print "\n", __doc__, | ||
| 42 | sys.exit(2) | ||
| 43 | |||
| 44 | def main_thread(port): | ||
| 45 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 46 | sock.bind(("", port)) | ||
| 47 | sock.listen(5) | ||
| 48 | print "Listening on port", port, "..." | ||
| 49 | while 1: | ||
| 50 | (conn, addr) = sock.accept() | ||
| 51 | if addr[0] != conn.getsockname()[0]: | ||
| 52 | conn.close() | ||
| 53 | print "Refusing connection from non-local host", addr[0], "." | ||
| 54 | continue | ||
| 55 | thread.start_new_thread(service_thread, (conn, addr)) | ||
| 56 | del conn, addr | ||
| 57 | |||
| 58 | def service_thread(conn, addr): | ||
| 59 | (caddr, cport) = addr | ||
| 60 | print "Thread %s has connection from %s.\n" % (str(thread.get_ident()), | ||
| 61 | caddr), | ||
| 62 | stdin = conn.makefile("r") | ||
| 63 | stdout = conn.makefile("w", 0) | ||
| 64 | run_interpreter(stdin, stdout) | ||
| 65 | print "Thread %s is done.\n" % str(thread.get_ident()), | ||
| 66 | |||
| 67 | def run_interpreter(stdin, stdout): | ||
| 68 | globals = {} | ||
| 69 | try: | ||
| 70 | str(sys.ps1) | ||
| 71 | except: | ||
| 72 | sys.ps1 = ">>> " | ||
| 73 | source = "" | ||
| 74 | while 1: | ||
| 75 | stdout.write(sys.ps1) | ||
| 76 | line = stdin.readline() | ||
| 77 | if line[:2] == '\377\354': | ||
| 78 | line = "" | ||
| 79 | if not line and not source: | ||
| 80 | break | ||
| 81 | if line[-2:] == '\r\n': | ||
| 82 | line = line[:-2] + '\n' | ||
| 83 | source = source + line | ||
| 84 | try: | ||
| 85 | code = compile_command(source) | ||
| 86 | except SyntaxError, err: | ||
| 87 | source = "" | ||
| 88 | traceback.print_exception(SyntaxError, err, None, file=stdout) | ||
| 89 | continue | ||
| 90 | if not code: | ||
| 91 | continue | ||
| 92 | source = "" | ||
| 93 | try: | ||
| 94 | run_command(code, stdin, stdout, globals) | ||
| 95 | except SystemExit, how: | ||
| 96 | if how: | ||
| 97 | try: | ||
| 98 | how = str(how) | ||
| 99 | except: | ||
| 100 | how = "" | ||
| 101 | stdout.write("Exit %s\n" % how) | ||
| 102 | break | ||
| 103 | stdout.write("\nGoodbye.\n") | ||
| 104 | |||
| 105 | def run_command(code, stdin, stdout, globals): | ||
| 106 | save = sys.stdin, sys.stdout, sys.stderr | ||
| 107 | try: | ||
| 108 | sys.stdout = sys.stderr = stdout | ||
| 109 | sys.stdin = stdin | ||
| 110 | try: | ||
| 111 | exec code in globals | ||
| 112 | except SystemExit, how: | ||
| 113 | raise SystemExit, how, sys.exc_info()[2] | ||
| 114 | except: | ||
| 115 | type, value, tb = sys.exc_info() | ||
| 116 | if tb: tb = tb.tb_next | ||
| 117 | traceback.print_exception(type, value, tb) | ||
| 118 | del tb | ||
| 119 | finally: | ||
| 120 | sys.stdin, sys.stdout, sys.stderr = save | ||
| 121 | |||
| 122 | from code import compile_command | ||
| 123 | |||
| 124 | main() | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | File Name Archive # Description | ||
| 2 | ----------------------------------------------------------- | ||
| 3 | MANIFEST 1 This shipping list | ||
| 4 | README 1 | ||
| 5 | T.py 1 | ||
| 6 | mountclient.py 1 | ||
| 7 | nfsclient.py 1 | ||
| 8 | rpc.py 1 | ||
| 9 | test 1 | ||
| 10 | xdr.py 1 | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | This is a Python interface to Sun RPC, designed and implemented mostly | ||
| 2 | by reading the Internet RFCs about the subject. | ||
| 3 | |||
| 4 | *** NOTE: xdr.py has evolved into the standard module xdrlib.py *** | ||
| 5 | |||
| 6 | There are two library modules, xdr.py and rpc.py, and several example | ||
| 7 | clients: mountclient.py, nfsclient.py, and rnusersclient.py, | ||
| 8 | implementing the NFS Mount protocol, (part of) the NFS protocol, and | ||
| 9 | the "rnusers" protocol (used by rusers(1)), respectively. The latter | ||
| 10 | demonstrates the use of broadcast via the Port mapper's CALLIT | ||
| 11 | procedure. | ||
| 12 | |||
| 13 | There is also a way to create servers in Python. | ||
| 14 | |||
| 15 | To test the nfs client, run it from the shell with something like this: | ||
| 16 | |||
| 17 | python -c 'import nfsclient; nfsclient.test()' [hostname [filesystemname]] | ||
| 18 | |||
| 19 | When called without a filesystemname, it lists the filesystems at the | ||
| 20 | host; default host is the local machine. | ||
| 21 | |||
| 22 | Other clients are tested similarly. | ||
| 23 | |||
| 24 | For hostname, use e.g. wuarchive.wustl.edu or gatekeeper.dec.com (two | ||
| 25 | hosts that are known to export NFS filesystems with little restrictions). | ||
| 26 | |||
| 27 | There are now two different RPC compilers: | ||
| 28 | |||
| 29 | 1) Wim Lewis rpcgen.py found on http://www.omnigroup.com/~wiml/soft/stale-index.html#python. | ||
| 30 | |||
| 31 | 2) Peter �strands rpcgen.py, which is part of "pynfs" (http://www.cendio.se/~peter/pynfs/). | ||
| @@ -0,0 +1,22 @@ | |||
| 1 | # Simple interface to report execution times of program fragments. | ||
| 2 | # Call TSTART() to reset the timer, TSTOP(...) to report times. | ||
| 3 | |||
| 4 | import sys, os, time | ||
| 5 | |||
| 6 | def TSTART(): | ||
| 7 | global t0, t1 | ||
| 8 | u, s, cu, cs = os.times() | ||
| 9 | t0 = u+cu, s+cs, time.time() | ||
| 10 | |||
| 11 | def TSTOP(*label): | ||
| 12 | global t0, t1 | ||
| 13 | u, s, cu, cs = os.times() | ||
| 14 | t1 = u+cu, s+cs, time.time() | ||
| 15 | tt = [] | ||
| 16 | for i in range(3): | ||
| 17 | tt.append(t1[i] - t0[i]) | ||
| 18 | [u, s, r] = tt | ||
| 19 | msg = '' | ||
| 20 | for x in label: msg = msg + (x + ' ') | ||
| 21 | msg = msg + '%r user, %r sys, %r real\n' % (u, s, r) | ||
| 22 | sys.stderr.write(msg) | ||
| @@ -0,0 +1,202 @@ | |||
| 1 | # Mount RPC client -- RFC 1094 (NFS), Appendix A | ||
| 2 | |||
| 3 | # This module demonstrates how to write your own RPC client in Python. | ||
| 4 | # When this example was written, there was no RPC compiler for | ||
| 5 | # Python. Without such a compiler, you must first create classes | ||
| 6 | # derived from Packer and Unpacker to handle the data types for the | ||
| 7 | # server you want to interface to. You then write the client class. | ||
| 8 | # If you want to support both the TCP and the UDP version of a | ||
| 9 | # protocol, use multiple inheritance as shown below. | ||
| 10 | |||
| 11 | |||
| 12 | import rpc | ||
| 13 | from rpc import Packer, Unpacker, TCPClient, UDPClient | ||
| 14 | |||
| 15 | |||
| 16 | # Program number and version for the mount protocol | ||
| 17 | MOUNTPROG = 100005 | ||
| 18 | MOUNTVERS = 1 | ||
| 19 | |||
| 20 | # Size of the 'fhandle' opaque structure | ||
| 21 | FHSIZE = 32 | ||
| 22 | |||
| 23 | |||
| 24 | # Packer derived class for Mount protocol clients. | ||
| 25 | # The only thing we need to pack beyond basic types is an 'fhandle' | ||
| 26 | |||
| 27 | class MountPacker(Packer): | ||
| 28 | |||
| 29 | def pack_fhandle(self, fhandle): | ||
| 30 | self.pack_fopaque(FHSIZE, fhandle) | ||
| 31 | |||
| 32 | |||
| 33 | # Unpacker derived class for Mount protocol clients. | ||
| 34 | # The important types we need to unpack are fhandle, fhstatus, | ||
| 35 | # mountlist and exportlist; mountstruct, exportstruct and groups are | ||
| 36 | # used to unpack components of mountlist and exportlist and the | ||
| 37 | # corresponding functions are passed as function argument to the | ||
| 38 | # generic unpack_list function. | ||
| 39 | |||
| 40 | class MountUnpacker(Unpacker): | ||
| 41 | |||
| 42 | def unpack_fhandle(self): | ||
| 43 | return self.unpack_fopaque(FHSIZE) | ||
| 44 | |||
| 45 | def unpack_fhstatus(self): | ||
| 46 | status = self.unpack_uint() | ||
| 47 | if status == 0: | ||
| 48 | fh = self.unpack_fhandle() | ||
| 49 | else: | ||
| 50 | fh = None | ||
| 51 | return status, fh | ||
| 52 | |||
| 53 | def unpack_mountlist(self): | ||
| 54 | return self.unpack_list(self.unpack_mountstruct) | ||
| 55 | |||
| 56 | def unpack_mountstruct(self): | ||
| 57 | hostname = self.unpack_string() | ||
| 58 | directory = self.unpack_string() | ||
| 59 | return (hostname, directory) | ||
| 60 | |||
| 61 | def unpack_exportlist(self): | ||
| 62 | return self.unpack_list(self.unpack_exportstruct) | ||
| 63 | |||
| 64 | def unpack_exportstruct(self): | ||
| 65 | filesys = self.unpack_string() | ||
| 66 | groups = self.unpack_groups() | ||
| 67 | return (filesys, groups) | ||
| 68 | |||
| 69 | def unpack_groups(self): | ||
| 70 | return self.unpack_list(self.unpack_string) | ||
| 71 | |||
| 72 | |||
| 73 | # These are the procedures specific to the Mount client class. | ||
| 74 | # Think of this as a derived class of either TCPClient or UDPClient. | ||
| 75 | |||
| 76 | class PartialMountClient: | ||
| 77 | |||
| 78 | # This method is called by Client.__init__ to initialize | ||
| 79 | # self.packer and self.unpacker | ||
| 80 | def addpackers(self): | ||
| 81 | self.packer = MountPacker() | ||
| 82 | self.unpacker = MountUnpacker('') | ||
| 83 | |||
| 84 | # This method is called by Client.__init__ to bind the socket | ||
| 85 | # to a particular network interface and port. We use the | ||
| 86 | # default network interface, but if we're running as root, | ||
| 87 | # we want to bind to a reserved port | ||
| 88 | def bindsocket(self): | ||
| 89 | import os | ||
| 90 | try: | ||
| 91 | uid = os.getuid() | ||
| 92 | except AttributeError: | ||
| 93 | uid = 1 | ||
| 94 | if uid == 0: | ||
| 95 | port = rpc.bindresvport(self.sock, '') | ||
| 96 | # 'port' is not used | ||
| 97 | else: | ||
| 98 | self.sock.bind(('', 0)) | ||
| 99 | |||
| 100 | # This function is called to cough up a suitable | ||
| 101 | # authentication object for a call to procedure 'proc'. | ||
| 102 | def mkcred(self): | ||
| 103 | if self.cred == None: | ||
| 104 | self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default() | ||
| 105 | return self.cred | ||
| 106 | |||
| 107 | # The methods Mnt, Dump etc. each implement one Remote | ||
| 108 | # Procedure Call. This is done by calling self.make_call() | ||
| 109 | # with as arguments: | ||
| 110 | # | ||
| 111 | # - the procedure number | ||
| 112 | # - the arguments (or None) | ||
| 113 | # - the "packer" function for the arguments (or None) | ||
| 114 | # - the "unpacker" function for the return value (or None) | ||
| 115 | # | ||
| 116 | # The packer and unpacker function, if not None, *must* be | ||
| 117 | # methods of self.packer and self.unpacker, respectively. | ||
| 118 | # A value of None means that there are no arguments or is no | ||
| 119 | # return value, respectively. | ||
| 120 | # | ||
| 121 | # The return value from make_call() is the return value from | ||
| 122 | # the remote procedure call, as unpacked by the "unpacker" | ||
| 123 | # function, or None if the unpacker function is None. | ||
| 124 | # | ||
| 125 | # (Even if you expect a result of None, you should still | ||
| 126 | # return the return value from make_call(), since this may be | ||
| 127 | # needed by a broadcasting version of the class.) | ||
| 128 | # | ||
| 129 | # If the call fails, make_call() raises an exception | ||
| 130 | # (this includes time-outs and invalid results). | ||
| 131 | # | ||
| 132 | # Note that (at least with the UDP protocol) there is no | ||
| 133 | # guarantee that a call is executed at most once. When you do | ||
| 134 | # get a reply, you know it has been executed at least once; | ||
| 135 | # when you don't get a reply, you know nothing. | ||
| 136 | |||
| 137 | def Mnt(self, directory): | ||
| 138 | return self.make_call(1, directory, \ | ||
| 139 | self.packer.pack_string, \ | ||
| 140 | self.unpacker.unpack_fhstatus) | ||
| 141 | |||
| 142 | def Dump(self): | ||
| 143 | return self.make_call(2, None, \ | ||
| 144 | None, self.unpacker.unpack_mountlist) | ||
| 145 | |||
| 146 | def Umnt(self, directory): | ||
| 147 | return self.make_call(3, directory, \ | ||
| 148 | self.packer.pack_string, None) | ||
| 149 | |||
| 150 | def Umntall(self): | ||
| 151 | return self.make_call(4, None, None, None) | ||
| 152 | |||
| 153 | def Export(self): | ||
| 154 | return self.make_call(5, None, \ | ||
| 155 | None, self.unpacker.unpack_exportlist) | ||
| 156 | |||
| 157 | |||
| 158 | # We turn the partial Mount client into a full one for either protocol | ||
| 159 | # by use of multiple inheritance. (In general, when class C has base | ||
| 160 | # classes B1...Bn, if x is an instance of class C, methods of x are | ||
| 161 | # searched first in C, then in B1, then in B2, ..., finally in Bn.) | ||
| 162 | |||
| 163 | class TCPMountClient(PartialMountClient, TCPClient): | ||
| 164 | |||
| 165 | def __init__(self, host): | ||
| 166 | TCPClient.__init__(self, host, MOUNTPROG, MOUNTVERS) | ||
| 167 | |||
| 168 | |||
| 169 | class UDPMountClient(PartialMountClient, UDPClient): | ||
| 170 | |||
| 171 | def __init__(self, host): | ||
| 172 | UDPClient.__init__(self, host, MOUNTPROG, MOUNTVERS) | ||
| 173 | |||
| 174 | |||
| 175 | # A little test program for the Mount client. This takes a host as | ||
| 176 | # command line argument (default the local machine), prints its export | ||
| 177 | # list, and attempts to mount and unmount each exported files system. | ||
| 178 | # An optional first argument of -t or -u specifies the protocol to use | ||
| 179 | # (TCP or UDP), default is UDP. | ||
| 180 | |||
| 181 | def test(): | ||
| 182 | import sys | ||
| 183 | if sys.argv[1:] and sys.argv[1] == '-t': | ||
| 184 | C = TCPMountClient | ||
| 185 | del sys.argv[1] | ||
| 186 | elif sys.argv[1:] and sys.argv[1] == '-u': | ||
| 187 | C = UDPMountClient | ||
| 188 | del sys.argv[1] | ||
| 189 | else: | ||
| 190 | C = UDPMountClient | ||
| 191 | if sys.argv[1:]: host = sys.argv[1] | ||
| 192 | else: host = '' | ||
| 193 | mcl = C(host) | ||
| 194 | list = mcl.Export() | ||
| 195 | for item in list: | ||
| 196 | print item | ||
| 197 | try: | ||
| 198 | mcl.Mnt(item[0]) | ||
| 199 | except: | ||
| 200 | print 'Sorry' | ||
| 201 | continue | ||
| 202 | mcl.Umnt(item[0]) | ||
| @@ -0,0 +1,201 @@ | |||
| 1 | # NFS RPC client -- RFC 1094 | ||
| 2 | |||
| 3 | # XXX This is not yet complete. | ||
| 4 | # XXX Only GETATTR, SETTTR, LOOKUP and READDIR are supported. | ||
| 5 | |||
| 6 | # (See mountclient.py for some hints on how to write RPC clients in | ||
| 7 | # Python in general) | ||
| 8 | |||
| 9 | import rpc | ||
| 10 | from rpc import UDPClient, TCPClient | ||
| 11 | from mountclient import FHSIZE, MountPacker, MountUnpacker | ||
| 12 | |||
| 13 | NFS_PROGRAM = 100003 | ||
| 14 | NFS_VERSION = 2 | ||
| 15 | |||
| 16 | # enum stat | ||
| 17 | NFS_OK = 0 | ||
| 18 | # (...many error values...) | ||
| 19 | |||
| 20 | # enum ftype | ||
| 21 | NFNON = 0 | ||
| 22 | NFREG = 1 | ||
| 23 | NFDIR = 2 | ||
| 24 | NFBLK = 3 | ||
| 25 | NFCHR = 4 | ||
| 26 | NFLNK = 5 | ||
| 27 | |||
| 28 | |||
| 29 | class NFSPacker(MountPacker): | ||
| 30 | |||
| 31 | def pack_sattrargs(self, sa): | ||
| 32 | file, attributes = sa | ||
| 33 | self.pack_fhandle(file) | ||
| 34 | self.pack_sattr(attributes) | ||
| 35 | |||
| 36 | def pack_sattr(self, sa): | ||
| 37 | mode, uid, gid, size, atime, mtime = sa | ||
| 38 | self.pack_uint(mode) | ||
| 39 | self.pack_uint(uid) | ||
| 40 | self.pack_uint(gid) | ||
| 41 | self.pack_uint(size) | ||
| 42 | self.pack_timeval(atime) | ||
| 43 | self.pack_timeval(mtime) | ||
| 44 | |||
| 45 | def pack_diropargs(self, da): | ||
| 46 | dir, name = da | ||
| 47 | self.pack_fhandle(dir) | ||
| 48 | self.pack_string(name) | ||
| 49 | |||
| 50 | def pack_readdirargs(self, ra): | ||
| 51 | dir, cookie, count = ra | ||
| 52 | self.pack_fhandle(dir) | ||
| 53 | self.pack_uint(cookie) | ||
| 54 | self.pack_uint(count) | ||
| 55 | |||
| 56 | def pack_timeval(self, tv): | ||
| 57 | secs, usecs = tv | ||
| 58 | self.pack_uint(secs) | ||
| 59 | self.pack_uint(usecs) | ||
| 60 | |||
| 61 | |||
| 62 | class NFSUnpacker(MountUnpacker): | ||
| 63 | |||
| 64 | def unpack_readdirres(self): | ||
| 65 | status = self.unpack_enum() | ||
| 66 | if status == NFS_OK: | ||
| 67 | entries = self.unpack_list(self.unpack_entry) | ||
| 68 | eof = self.unpack_bool() | ||
| 69 | rest = (entries, eof) | ||
| 70 | else: | ||
| 71 | rest = None | ||
| 72 | return (status, rest) | ||
| 73 | |||
| 74 | def unpack_entry(self): | ||
| 75 | fileid = self.unpack_uint() | ||
| 76 | name = self.unpack_string() | ||
| 77 | cookie = self.unpack_uint() | ||
| 78 | return (fileid, name, cookie) | ||
| 79 | |||
| 80 | def unpack_diropres(self): | ||
| 81 | status = self.unpack_enum() | ||
| 82 | if status == NFS_OK: | ||
| 83 | fh = self.unpack_fhandle() | ||
| 84 | fa = self.unpack_fattr() | ||
| 85 | rest = (fh, fa) | ||
| 86 | else: | ||
| 87 | rest = None | ||
| 88 | return (status, rest) | ||
| 89 | |||
| 90 | def unpack_attrstat(self): | ||
| 91 | status = self.unpack_enum() | ||
| 92 | if status == NFS_OK: | ||
| 93 | attributes = self.unpack_fattr() | ||
| 94 | else: | ||
| 95 | attributes = None | ||
| 96 | return status, attributes | ||
| 97 | |||
| 98 | def unpack_fattr(self): | ||
| 99 | type = self.unpack_enum() | ||
| 100 | mode = self.unpack_uint() | ||
| 101 | nlink = self.unpack_uint() | ||
| 102 | uid = self.unpack_uint() | ||
| 103 | gid = self.unpack_uint() | ||
| 104 | size = self.unpack_uint() | ||
| 105 | blocksize = self.unpack_uint() | ||
| 106 | rdev = self.unpack_uint() | ||
| 107 | blocks = self.unpack_uint() | ||
| 108 | fsid = self.unpack_uint() | ||
| 109 | fileid = self.unpack_uint() | ||
| 110 | atime = self.unpack_timeval() | ||
| 111 | mtime = self.unpack_timeval() | ||
| 112 | ctime = self.unpack_timeval() | ||
| 113 | return (type, mode, nlink, uid, gid, size, blocksize, \ | ||
| 114 | rdev, blocks, fsid, fileid, atime, mtime, ctime) | ||
| 115 | |||
| 116 | def unpack_timeval(self): | ||
| 117 | secs = self.unpack_uint() | ||
| 118 | usecs = self.unpack_uint() | ||
| 119 | return (secs, usecs) | ||
| 120 | |||
| 121 | |||
| 122 | class NFSClient(UDPClient): | ||
| 123 | |||
| 124 | def __init__(self, host): | ||
| 125 | UDPClient.__init__(self, host, NFS_PROGRAM, NFS_VERSION) | ||
| 126 | |||
| 127 | def addpackers(self): | ||
| 128 | self.packer = NFSPacker() | ||
| 129 | self.unpacker = NFSUnpacker('') | ||
| 130 | |||
| 131 | def mkcred(self): | ||
| 132 | if self.cred == None: | ||
| 133 | self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default() | ||
| 134 | return self.cred | ||
| 135 | |||
| 136 | def Getattr(self, fh): | ||
| 137 | return self.make_call(1, fh, \ | ||
| 138 | self.packer.pack_fhandle, \ | ||
| 139 | self.unpacker.unpack_attrstat) | ||
| 140 | |||
| 141 | def Setattr(self, sa): | ||
| 142 | return self.make_call(2, sa, \ | ||
| 143 | self.packer.pack_sattrargs, \ | ||
| 144 | self.unpacker.unpack_attrstat) | ||
| 145 | |||
| 146 | # Root() is obsolete | ||
| 147 | |||
| 148 | def Lookup(self, da): | ||
| 149 | return self.make_call(4, da, \ | ||
| 150 | self.packer.pack_diropargs, \ | ||
| 151 | self.unpacker.unpack_diropres) | ||
| 152 | |||
| 153 | # ... | ||
| 154 | |||
| 155 | def Readdir(self, ra): | ||
| 156 | return self.make_call(16, ra, \ | ||
| 157 | self.packer.pack_readdirargs, \ | ||
| 158 | self.unpacker.unpack_readdirres) | ||
| 159 | |||
| 160 | # Shorthand to get the entire contents of a directory | ||
| 161 | def Listdir(self, dir): | ||
| 162 | list = [] | ||
| 163 | ra = (dir, 0, 2000) | ||
| 164 | while 1: | ||
| 165 | (status, rest) = self.Readdir(ra) | ||
| 166 | if status <> NFS_OK: | ||
| 167 | break | ||
| 168 | entries, eof = rest | ||
| 169 | last_cookie = None | ||
| 170 | for fileid, name, cookie in entries: | ||
| 171 | list.append((fileid, name)) | ||
| 172 | last_cookie = cookie | ||
| 173 | if eof or last_cookie == None: | ||
| 174 | break | ||
| 175 | ra = (ra[0], last_cookie, ra[2]) | ||
| 176 | return list | ||
| 177 | |||
| 178 | |||
| 179 | def test(): | ||
| 180 | import sys | ||
| 181 | if sys.argv[1:]: host = sys.argv[1] | ||
| 182 | else: host = '' | ||
| 183 | if sys.argv[2:]: filesys = sys.argv[2] | ||
| 184 | else: filesys = None | ||
| 185 | from mountclient import UDPMountClient, TCPMountClient | ||
| 186 | mcl = TCPMountClient(host) | ||
| 187 | if filesys == None: | ||
| 188 | list = mcl.Export() | ||
| 189 | for item in list: | ||
| 190 | print item | ||
| 191 | return | ||
| 192 | sf = mcl.Mnt(filesys) | ||
| 193 | print sf | ||
| 194 | fh = sf[1] | ||
| 195 | if fh: | ||
| 196 | ncl = NFSClient(host) | ||
| 197 | as = ncl.Getattr(fh) | ||
| 198 | print as | ||
| 199 | list = ncl.Listdir(fh) | ||
| 200 | for item in list: print item | ||
| 201 | mcl.Umnt(filesys) | ||
| @@ -0,0 +1,98 @@ | |||
| 1 | # Remote nusers client interface | ||
| 2 | |||
| 3 | import rpc | ||
| 4 | from rpc import Packer, Unpacker, UDPClient, BroadcastUDPClient | ||
| 5 | |||
| 6 | |||
| 7 | class RnusersPacker(Packer): | ||
| 8 | def pack_utmp(self, ui): | ||
| 9 | ut_line, ut_name, ut_host, ut_time = utmp | ||
| 10 | self.pack_string(ut_line) | ||
| 11 | self.pack_string(ut_name) | ||
| 12 | self.pack_string(ut_host) | ||
| 13 | self.pack_int(ut_time) | ||
| 14 | def pack_utmpidle(self, ui): | ||
| 15 | ui_itmp, ui_idle = ui | ||
| 16 | self.pack_utmp(ui_utmp) | ||
| 17 | self.pack_uint(ui_idle) | ||
| 18 | def pack_utmpidlearr(self, list): | ||
| 19 | self.pack_array(list, self.pack_itmpidle) | ||
| 20 | |||
| 21 | |||
| 22 | class RnusersUnpacker(Unpacker): | ||
| 23 | def unpack_utmp(self): | ||
| 24 | ut_line = self.unpack_string() | ||
| 25 | ut_name = self.unpack_string() | ||
| 26 | ut_host = self.unpack_string() | ||
| 27 | ut_time = self.unpack_int() | ||
| 28 | return ut_line, ut_name, ut_host, ut_time | ||
| 29 | def unpack_utmpidle(self): | ||
| 30 | ui_utmp = self.unpack_utmp() | ||
| 31 | ui_idle = self.unpack_uint() | ||
| 32 | return ui_utmp, ui_idle | ||
| 33 | def unpack_utmpidlearr(self): | ||
| 34 | return self.unpack_array(self.unpack_utmpidle) | ||
| 35 | |||
| 36 | |||
| 37 | class PartialRnusersClient: | ||
| 38 | |||
| 39 | def addpackers(self): | ||
| 40 | self.packer = RnusersPacker() | ||
| 41 | self.unpacker = RnusersUnpacker('') | ||
| 42 | |||
| 43 | def Num(self): | ||
| 44 | return self.make_call(1, None, None, self.unpacker.unpack_int) | ||
| 45 | |||
| 46 | def Names(self): | ||
| 47 | return self.make_call(2, None, \ | ||
| 48 | None, self.unpacker.unpack_utmpidlearr) | ||
| 49 | |||
| 50 | def Allnames(self): | ||
| 51 | return self.make_call(3, None, \ | ||
| 52 | None, self.unpacker.unpack_utmpidlearr) | ||
| 53 | |||
| 54 | |||
| 55 | class RnusersClient(PartialRnusersClient, UDPClient): | ||
| 56 | |||
| 57 | def __init__(self, host): | ||
| 58 | UDPClient.__init__(self, host, 100002, 2) | ||
| 59 | |||
| 60 | |||
| 61 | class BroadcastRnusersClient(PartialRnusersClient, BroadcastUDPClient): | ||
| 62 | |||
| 63 | def __init__(self, bcastaddr): | ||
| 64 | BroadcastUDPClient.__init__(self, bcastaddr, 100002, 2) | ||
| 65 | |||
| 66 | |||
| 67 | def test(): | ||
| 68 | import sys | ||
| 69 | if not sys.argv[1:]: | ||
| 70 | testbcast() | ||
| 71 | return | ||
| 72 | else: | ||
| 73 | host = sys.argv[1] | ||
| 74 | c = RnusersClient(host) | ||
| 75 | list = c.Names() | ||
| 76 | for (line, name, host, time), idle in list: | ||
| 77 | line = strip0(line) | ||
| 78 | name = strip0(name) | ||
| 79 | host = strip0(host) | ||
| 80 | print "%r %r %r %s %s" % (name, host, line, time, idle) | ||
| 81 | |||
| 82 | def testbcast(): | ||
| 83 | c = BroadcastRnusersClient('<broadcast>') | ||
| 84 | def listit(list, fromaddr): | ||
| 85 | host, port = fromaddr | ||
| 86 | print host + '\t:', | ||
| 87 | for (line, name, host, time), idle in list: | ||
| 88 | print strip0(name), | ||
| 89 | |||
| 90 | c.set_reply_handler(listit) | ||
| 91 | all = c.Names() | ||
| 92 | print 'Total Count:', len(all) | ||
| 93 | |||
| 94 | def strip0(s): | ||
| 95 | while s and s[-1] == '\0': s = s[:-1] | ||
| 96 | return s | ||
| 97 | |||
| 98 | test() | ||
| @@ -0,0 +1,893 @@ | |||
| 1 | # Sun RPC version 2 -- RFC1057. | ||
| 2 | |||
| 3 | # XXX There should be separate exceptions for the various reasons why | ||
| 4 | # XXX an RPC can fail, rather than using RuntimeError for everything | ||
| 5 | |||
| 6 | # XXX Need to use class based exceptions rather than string exceptions | ||
| 7 | |||
| 8 | # XXX The UDP version of the protocol resends requests when it does | ||
| 9 | # XXX not receive a timely reply -- use only for idempotent calls! | ||
| 10 | |||
| 11 | # XXX There is no provision for call timeout on TCP connections | ||
| 12 | |||
| 13 | import xdr | ||
| 14 | import socket | ||
| 15 | import os | ||
| 16 | |||
| 17 | RPCVERSION = 2 | ||
| 18 | |||
| 19 | CALL = 0 | ||
| 20 | REPLY = 1 | ||
| 21 | |||
| 22 | AUTH_NULL = 0 | ||
| 23 | AUTH_UNIX = 1 | ||
| 24 | AUTH_SHORT = 2 | ||
| 25 | AUTH_DES = 3 | ||
| 26 | |||
| 27 | MSG_ACCEPTED = 0 | ||
| 28 | MSG_DENIED = 1 | ||
| 29 | |||
| 30 | SUCCESS = 0 # RPC executed successfully | ||
| 31 | PROG_UNAVAIL = 1 # remote hasn't exported program | ||
| 32 | PROG_MISMATCH = 2 # remote can't support version # | ||
| 33 | PROC_UNAVAIL = 3 # program can't support procedure | ||
| 34 | GARBAGE_ARGS = 4 # procedure can't decode params | ||
| 35 | |||
| 36 | RPC_MISMATCH = 0 # RPC version number != 2 | ||
| 37 | AUTH_ERROR = 1 # remote can't authenticate caller | ||
| 38 | |||
| 39 | AUTH_BADCRED = 1 # bad credentials (seal broken) | ||
| 40 | AUTH_REJECTEDCRED = 2 # client must begin new session | ||
| 41 | AUTH_BADVERF = 3 # bad verifier (seal broken) | ||
| 42 | AUTH_REJECTEDVERF = 4 # verifier expired or replayed | ||
| 43 | AUTH_TOOWEAK = 5 # rejected for security reasons | ||
| 44 | |||
| 45 | |||
| 46 | class Packer(xdr.Packer): | ||
| 47 | |||
| 48 | def pack_auth(self, auth): | ||
| 49 | flavor, stuff = auth | ||
| 50 | self.pack_enum(flavor) | ||
| 51 | self.pack_opaque(stuff) | ||
| 52 | |||
| 53 | def pack_auth_unix(self, stamp, machinename, uid, gid, gids): | ||
| 54 | self.pack_uint(stamp) | ||
| 55 | self.pack_string(machinename) | ||
| 56 | self.pack_uint(uid) | ||
| 57 | self.pack_uint(gid) | ||
| 58 | self.pack_uint(len(gids)) | ||
| 59 | for i in gids: | ||
| 60 | self.pack_uint(i) | ||
| 61 | |||
| 62 | def pack_callheader(self, xid, prog, vers, proc, cred, verf): | ||
| 63 | self.pack_uint(xid) | ||
| 64 | self.pack_enum(CALL) | ||
| 65 | self.pack_uint(RPCVERSION) | ||
| 66 | self.pack_uint(prog) | ||
| 67 | self.pack_uint(vers) | ||
| 68 | self.pack_uint(proc) | ||
| 69 | self.pack_auth(cred) | ||
| 70 | self.pack_auth(verf) | ||
| 71 | # Caller must add procedure-specific part of call | ||
| 72 | |||
| 73 | def pack_replyheader(self, xid, verf): | ||
| 74 | self.pack_uint(xid) | ||
| 75 | self.pack_enum(REPLY) | ||
| 76 | self.pack_uint(MSG_ACCEPTED) | ||
| 77 | self.pack_auth(verf) | ||
| 78 | self.pack_enum(SUCCESS) | ||
| 79 | # Caller must add procedure-specific part of reply | ||
| 80 | |||
| 81 | |||
| 82 | # Exceptions | ||
| 83 | BadRPCFormat = 'rpc.BadRPCFormat' | ||
| 84 | BadRPCVersion = 'rpc.BadRPCVersion' | ||
| 85 | GarbageArgs = 'rpc.GarbageArgs' | ||
| 86 | |||
| 87 | class Unpacker(xdr.Unpacker): | ||
| 88 | |||
| 89 | def unpack_auth(self): | ||
| 90 | flavor = self.unpack_enum() | ||
| 91 | stuff = self.unpack_opaque() | ||
| 92 | return (flavor, stuff) | ||
| 93 | |||
| 94 | def unpack_callheader(self): | ||
| 95 | xid = self.unpack_uint() | ||
| 96 | temp = self.unpack_enum() | ||
| 97 | if temp != CALL: | ||
| 98 | raise BadRPCFormat, 'no CALL but %r' % (temp,) | ||
| 99 | temp = self.unpack_uint() | ||
| 100 | if temp != RPCVERSION: | ||
| 101 | raise BadRPCVersion, 'bad RPC version %r' % (temp,) | ||
| 102 | prog = self.unpack_uint() | ||
| 103 | vers = self.unpack_uint() | ||
| 104 | proc = self.unpack_uint() | ||
| 105 | cred = self.unpack_auth() | ||
| 106 | verf = self.unpack_auth() | ||
| 107 | return xid, prog, vers, proc, cred, verf | ||
| 108 | # Caller must add procedure-specific part of call | ||
| 109 | |||
| 110 | def unpack_replyheader(self): | ||
| 111 | xid = self.unpack_uint() | ||
| 112 | mtype = self.unpack_enum() | ||
| 113 | if mtype != REPLY: | ||
| 114 | raise RuntimeError, 'no REPLY but %r' % (mtype,) | ||
| 115 | stat = self.unpack_enum() | ||
| 116 | if stat == MSG_DENIED: | ||
| 117 | stat = self.unpack_enum() | ||
| 118 | if stat == RPC_MISMATCH: | ||
| 119 | low = self.unpack_uint() | ||
| 120 | high = self.unpack_uint() | ||
| 121 | raise RuntimeError, \ | ||
| 122 | 'MSG_DENIED: RPC_MISMATCH: %r' % ((low, high),) | ||
| 123 | if stat == AUTH_ERROR: | ||
| 124 | stat = self.unpack_uint() | ||
| 125 | raise RuntimeError, \ | ||
| 126 | 'MSG_DENIED: AUTH_ERROR: %r' % (stat,) | ||
| 127 | raise RuntimeError, 'MSG_DENIED: %r' % (stat,) | ||
| 128 | if stat != MSG_ACCEPTED: | ||
| 129 | raise RuntimeError, \ | ||
| 130 | 'Neither MSG_DENIED nor MSG_ACCEPTED: %r' % (stat,) | ||
| 131 | verf = self.unpack_auth() | ||
| 132 | stat = self.unpack_enum() | ||
| 133 | if stat == PROG_UNAVAIL: | ||
| 134 | raise RuntimeError, 'call failed: PROG_UNAVAIL' | ||
| 135 | if stat == PROG_MISMATCH: | ||
| 136 | low = self.unpack_uint() | ||
| 137 | high = self.unpack_uint() | ||
| 138 | raise RuntimeError, \ | ||
| 139 | 'call failed: PROG_MISMATCH: %r' % ((low, high),) | ||
| 140 | if stat == PROC_UNAVAIL: | ||
| 141 | raise RuntimeError, 'call failed: PROC_UNAVAIL' | ||
| 142 | if stat == GARBAGE_ARGS: | ||
| 143 | raise RuntimeError, 'call failed: GARBAGE_ARGS' | ||
| 144 | if stat != SUCCESS: | ||
| 145 | raise RuntimeError, 'call failed: %r' % (stat,) | ||
| 146 | return xid, verf | ||
| 147 | # Caller must get procedure-specific part of reply | ||
| 148 | |||
| 149 | |||
| 150 | # Subroutines to create opaque authentication objects | ||
| 151 | |||
| 152 | def make_auth_null(): | ||
| 153 | return '' | ||
| 154 | |||
| 155 | def make_auth_unix(seed, host, uid, gid, groups): | ||
| 156 | p = Packer() | ||
| 157 | p.pack_auth_unix(seed, host, uid, gid, groups) | ||
| 158 | return p.get_buf() | ||
| 159 | |||
| 160 | def make_auth_unix_default(): | ||
| 161 | try: | ||
| 162 | from os import getuid, getgid | ||
| 163 | uid = getuid() | ||
| 164 | gid = getgid() | ||
| 165 | except ImportError: | ||
| 166 | uid = gid = 0 | ||
| 167 | import time | ||
| 168 | return make_auth_unix(int(time.time()-unix_epoch()), \ | ||
| 169 | socket.gethostname(), uid, gid, []) | ||
| 170 | |||
| 171 | _unix_epoch = -1 | ||
| 172 | def unix_epoch(): | ||
| 173 | """Very painful calculation of when the Unix Epoch is. | ||
| 174 | |||
| 175 | This is defined as the return value of time.time() on Jan 1st, | ||
| 176 | 1970, 00:00:00 GMT. | ||
| 177 | |||
| 178 | On a Unix system, this should always return 0.0. On a Mac, the | ||
| 179 | calculations are needed -- and hard because of integer overflow | ||
| 180 | and other limitations. | ||
| 181 | |||
| 182 | """ | ||
| 183 | global _unix_epoch | ||
| 184 | if _unix_epoch >= 0: return _unix_epoch | ||
| 185 | import time | ||
| 186 | now = time.time() | ||
| 187 | localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...) | ||
| 188 | gmt = time.gmtime(now) | ||
| 189 | offset = time.mktime(localt) - time.mktime(gmt) | ||
| 190 | y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0 | ||
| 191 | offset, ss = divmod(ss + offset, 60) | ||
| 192 | offset, mm = divmod(mm + offset, 60) | ||
| 193 | offset, hh = divmod(hh + offset, 24) | ||
| 194 | d = d + offset | ||
| 195 | _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0)) | ||
| 196 | print "Unix epoch:", time.ctime(_unix_epoch) | ||
| 197 | return _unix_epoch | ||
| 198 | |||
| 199 | |||
| 200 | # Common base class for clients | ||
| 201 | |||
| 202 | class Client: | ||
| 203 | |||
| 204 | def __init__(self, host, prog, vers, port): | ||
| 205 | self.host = host | ||
| 206 | self.prog = prog | ||
| 207 | self.vers = vers | ||
| 208 | self.port = port | ||
| 209 | self.makesocket() # Assigns to self.sock | ||
| 210 | self.bindsocket() | ||
| 211 | self.connsocket() | ||
| 212 | self.lastxid = 0 # XXX should be more random? | ||
| 213 | self.addpackers() | ||
| 214 | self.cred = None | ||
| 215 | self.verf = None | ||
| 216 | |||
| 217 | def close(self): | ||
| 218 | self.sock.close() | ||
| 219 | |||
| 220 | def makesocket(self): | ||
| 221 | # This MUST be overridden | ||
| 222 | raise RuntimeError, 'makesocket not defined' | ||
| 223 | |||
| 224 | def connsocket(self): | ||
| 225 | # Override this if you don't want/need a connection | ||
| 226 | self.sock.connect((self.host, self.port)) | ||
| 227 | |||
| 228 | def bindsocket(self): | ||
| 229 | # Override this to bind to a different port (e.g. reserved) | ||
| 230 | self.sock.bind(('', 0)) | ||
| 231 | |||
| 232 | def addpackers(self): | ||
| 233 | # Override this to use derived classes from Packer/Unpacker | ||
| 234 | self.packer = Packer() | ||
| 235 | self.unpacker = Unpacker('') | ||
| 236 | |||
| 237 | def make_call(self, proc, args, pack_func, unpack_func): | ||
| 238 | # Don't normally override this (but see Broadcast) | ||
| 239 | if pack_func is None and args is not None: | ||
| 240 | raise TypeError, 'non-null args with null pack_func' | ||
| 241 | self.start_call(proc) | ||
| 242 | if pack_func: | ||
| 243 | pack_func(args) | ||
| 244 | self.do_call() | ||
| 245 | if unpack_func: | ||
| 246 | result = unpack_func() | ||
| 247 | else: | ||
| 248 | result = None | ||
| 249 | self.unpacker.done() | ||
| 250 | return result | ||
| 251 | |||
| 252 | def start_call(self, proc): | ||
| 253 | # Don't override this | ||
| 254 | self.lastxid = xid = self.lastxid + 1 | ||
| 255 | cred = self.mkcred() | ||
| 256 | verf = self.mkverf() | ||
| 257 | p = self.packer | ||
| 258 | p.reset() | ||
| 259 | p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) | ||
| 260 | |||
| 261 | def do_call(self): | ||
| 262 | # This MUST be overridden | ||
| 263 | raise RuntimeError, 'do_call not defined' | ||
| 264 | |||
| 265 | def mkcred(self): | ||
| 266 | # Override this to use more powerful credentials | ||
| 267 | if self.cred == None: | ||
| 268 | self.cred = (AUTH_NULL, make_auth_null()) | ||
| 269 | return self.cred | ||
| 270 | |||
| 271 | def mkverf(self): | ||
| 272 | # Override this to use a more powerful verifier | ||
| 273 | if self.verf == None: | ||
| 274 | self.verf = (AUTH_NULL, make_auth_null()) | ||
| 275 | return self.verf | ||
| 276 | |||
| 277 | def call_0(self): # Procedure 0 is always like this | ||
| 278 | return self.make_call(0, None, None, None) | ||
| 279 | |||
| 280 | |||
| 281 | # Record-Marking standard support | ||
| 282 | |||
| 283 | def sendfrag(sock, last, frag): | ||
| 284 | x = len(frag) | ||
| 285 | if last: x = x | 0x80000000L | ||
| 286 | header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \ | ||
| 287 | chr(int(x>>8 & 0xff)) + chr(int(x & 0xff))) | ||
| 288 | sock.send(header + frag) | ||
| 289 | |||
| 290 | def sendrecord(sock, record): | ||
| 291 | sendfrag(sock, 1, record) | ||
| 292 | |||
| 293 | def recvfrag(sock): | ||
| 294 | header = sock.recv(4) | ||
| 295 | if len(header) < 4: | ||
| 296 | raise EOFError | ||
| 297 | x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \ | ||
| 298 | ord(header[2])<<8 | ord(header[3]) | ||
| 299 | last = ((x & 0x80000000) != 0) | ||
| 300 | n = int(x & 0x7fffffff) | ||
| 301 | frag = '' | ||
| 302 | while n > 0: | ||
| 303 | buf = sock.recv(n) | ||
| 304 | if not buf: raise EOFError | ||
| 305 | n = n - len(buf) | ||
| 306 | frag = frag + buf | ||
| 307 | return last, frag | ||
| 308 | |||
| 309 | def recvrecord(sock): | ||
| 310 | record = '' | ||
| 311 | last = 0 | ||
| 312 | while not last: | ||
| 313 | last, frag = recvfrag(sock) | ||
| 314 | record = record + frag | ||
| 315 | return record | ||
| 316 | |||
| 317 | |||
| 318 | # Try to bind to a reserved port (must be root) | ||
| 319 | |||
| 320 | last_resv_port_tried = None | ||
| 321 | def bindresvport(sock, host): | ||
| 322 | global last_resv_port_tried | ||
| 323 | FIRST, LAST = 600, 1024 # Range of ports to try | ||
| 324 | if last_resv_port_tried == None: | ||
| 325 | import os | ||
| 326 | last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST) | ||
| 327 | for i in range(last_resv_port_tried, LAST) + \ | ||
| 328 | range(FIRST, last_resv_port_tried): | ||
| 329 | last_resv_port_tried = i | ||
| 330 | try: | ||
| 331 | sock.bind((host, i)) | ||
| 332 | return last_resv_port_tried | ||
| 333 | except socket.error, (errno, msg): | ||
| 334 | if errno != 114: | ||
| 335 | raise socket.error, (errno, msg) | ||
| 336 | raise RuntimeError, 'can\'t assign reserved port' | ||
| 337 | |||
| 338 | |||
| 339 | # Client using TCP to a specific port | ||
| 340 | |||
| 341 | class RawTCPClient(Client): | ||
| 342 | |||
| 343 | def makesocket(self): | ||
| 344 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 345 | |||
| 346 | def do_call(self): | ||
| 347 | call = self.packer.get_buf() | ||
| 348 | sendrecord(self.sock, call) | ||
| 349 | reply = recvrecord(self.sock) | ||
| 350 | u = self.unpacker | ||
| 351 | u.reset(reply) | ||
| 352 | xid, verf = u.unpack_replyheader() | ||
| 353 | if xid != self.lastxid: | ||
| 354 | # Can't really happen since this is TCP... | ||
| 355 | raise RuntimeError, 'wrong xid in reply %r instead of %r' % ( | ||
| 356 | xid, self.lastxid) | ||
| 357 | |||
| 358 | |||
| 359 | # Client using UDP to a specific port | ||
| 360 | |||
| 361 | class RawUDPClient(Client): | ||
| 362 | |||
| 363 | def makesocket(self): | ||
| 364 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||
| 365 | |||
| 366 | def do_call(self): | ||
| 367 | call = self.packer.get_buf() | ||
| 368 | self.sock.send(call) | ||
| 369 | try: | ||
| 370 | from select import select | ||
| 371 | except ImportError: | ||
| 372 | print 'WARNING: select not found, RPC may hang' | ||
| 373 | select = None | ||
| 374 | BUFSIZE = 8192 # Max UDP buffer size | ||
| 375 | timeout = 1 | ||
| 376 | count = 5 | ||
| 377 | while 1: | ||
| 378 | r, w, x = [self.sock], [], [] | ||
| 379 | if select: | ||
| 380 | r, w, x = select(r, w, x, timeout) | ||
| 381 | if self.sock not in r: | ||
| 382 | count = count - 1 | ||
| 383 | if count < 0: raise RuntimeError, 'timeout' | ||
| 384 | if timeout < 25: timeout = timeout *2 | ||
| 385 | ## print 'RESEND', timeout, count | ||
| 386 | self.sock.send(call) | ||
| 387 | continue | ||
| 388 | reply = self.sock.recv(BUFSIZE) | ||
| 389 | u = self.unpacker | ||
| 390 | u.reset(reply) | ||
| 391 | xid, verf = u.unpack_replyheader() | ||
| 392 | if xid != self.lastxid: | ||
| 393 | ## print 'BAD xid' | ||
| 394 | continue | ||
| 395 | break | ||
| 396 | |||
| 397 | |||
| 398 | # Client using UDP broadcast to a specific port | ||
| 399 | |||
| 400 | class RawBroadcastUDPClient(RawUDPClient): | ||
| 401 | |||
| 402 | def __init__(self, bcastaddr, prog, vers, port): | ||
| 403 | RawUDPClient.__init__(self, bcastaddr, prog, vers, port) | ||
| 404 | self.reply_handler = None | ||
| 405 | self.timeout = 30 | ||
| 406 | |||
| 407 | def connsocket(self): | ||
| 408 | # Don't connect -- use sendto | ||
| 409 | self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) | ||
| 410 | |||
| 411 | def set_reply_handler(self, reply_handler): | ||
| 412 | self.reply_handler = reply_handler | ||
| 413 | |||
| 414 | def set_timeout(self, timeout): | ||
| 415 | self.timeout = timeout # Use None for infinite timeout | ||
| 416 | |||
| 417 | def make_call(self, proc, args, pack_func, unpack_func): | ||
| 418 | if pack_func is None and args is not None: | ||
| 419 | raise TypeError, 'non-null args with null pack_func' | ||
| 420 | self.start_call(proc) | ||
| 421 | if pack_func: | ||
| 422 | pack_func(args) | ||
| 423 | call = self.packer.get_buf() | ||
| 424 | self.sock.sendto(call, (self.host, self.port)) | ||
| 425 | try: | ||
| 426 | from select import select | ||
| 427 | except ImportError: | ||
| 428 | print 'WARNING: select not found, broadcast will hang' | ||
| 429 | select = None | ||
| 430 | BUFSIZE = 8192 # Max UDP buffer size (for reply) | ||
| 431 | replies = [] | ||
| 432 | if unpack_func is None: | ||
| 433 | def dummy(): pass | ||
| 434 | unpack_func = dummy | ||
| 435 | while 1: | ||
| 436 | r, w, x = [self.sock], [], [] | ||
| 437 | if select: | ||
| 438 | if self.timeout is None: | ||
| 439 | r, w, x = select(r, w, x) | ||
| 440 | else: | ||
| 441 | r, w, x = select(r, w, x, self.timeout) | ||
| 442 | if self.sock not in r: | ||
| 443 | break | ||
| 444 | reply, fromaddr = self.sock.recvfrom(BUFSIZE) | ||
| 445 | u = self.unpacker | ||
| 446 | u.reset(reply) | ||
| 447 | xid, verf = u.unpack_replyheader() | ||
| 448 | if xid != self.lastxid: | ||
| 449 | ## print 'BAD xid' | ||
| 450 | continue | ||
| 451 | reply = unpack_func() | ||
| 452 | self.unpacker.done() | ||
| 453 | replies.append((reply, fromaddr)) | ||
| 454 | if self.reply_handler: | ||
| 455 | self.reply_handler(reply, fromaddr) | ||
| 456 | return replies | ||
| 457 | |||
| 458 | |||
| 459 | # Port mapper interface | ||
| 460 | |||
| 461 | # Program number, version and (fixed!) port number | ||
| 462 | PMAP_PROG = 100000 | ||
| 463 | PMAP_VERS = 2 | ||
| 464 | PMAP_PORT = 111 | ||
| 465 | |||
| 466 | # Procedure numbers | ||
| 467 | PMAPPROC_NULL = 0 # (void) -> void | ||
| 468 | PMAPPROC_SET = 1 # (mapping) -> bool | ||
| 469 | PMAPPROC_UNSET = 2 # (mapping) -> bool | ||
| 470 | PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int | ||
| 471 | PMAPPROC_DUMP = 4 # (void) -> pmaplist | ||
| 472 | PMAPPROC_CALLIT = 5 # (call_args) -> call_result | ||
| 473 | |||
| 474 | # A mapping is (prog, vers, prot, port) and prot is one of: | ||
| 475 | |||
| 476 | IPPROTO_TCP = 6 | ||
| 477 | IPPROTO_UDP = 17 | ||
| 478 | |||
| 479 | # A pmaplist is a variable-length list of mappings, as follows: | ||
| 480 | # either (1, mapping, pmaplist) or (0). | ||
| 481 | |||
| 482 | # A call_args is (prog, vers, proc, args) where args is opaque; | ||
| 483 | # a call_result is (port, res) where res is opaque. | ||
| 484 | |||
| 485 | |||
| 486 | class PortMapperPacker(Packer): | ||
| 487 | |||
| 488 | def pack_mapping(self, mapping): | ||
| 489 | prog, vers, prot, port = mapping | ||
| 490 | self.pack_uint(prog) | ||
| 491 | self.pack_uint(vers) | ||
| 492 | self.pack_uint(prot) | ||
| 493 | self.pack_uint(port) | ||
| 494 | |||
| 495 | def pack_pmaplist(self, list): | ||
| 496 | self.pack_list(list, self.pack_mapping) | ||
| 497 | |||
| 498 | def pack_call_args(self, ca): | ||
| 499 | prog, vers, proc, args = ca | ||
| 500 | self.pack_uint(prog) | ||
| 501 | self.pack_uint(vers) | ||
| 502 | self.pack_uint(proc) | ||
| 503 | self.pack_opaque(args) | ||
| 504 | |||
| 505 | |||
| 506 | class PortMapperUnpacker(Unpacker): | ||
| 507 | |||
| 508 | def unpack_mapping(self): | ||
| 509 | prog = self.unpack_uint() | ||
| 510 | vers = self.unpack_uint() | ||
| 511 | prot = self.unpack_uint() | ||
| 512 | port = self.unpack_uint() | ||
| 513 | return prog, vers, prot, port | ||
| 514 | |||
| 515 | def unpack_pmaplist(self): | ||
| 516 | return self.unpack_list(self.unpack_mapping) | ||
| 517 | |||
| 518 | def unpack_call_result(self): | ||
| 519 | port = self.unpack_uint() | ||
| 520 | res = self.unpack_opaque() | ||
| 521 | return port, res | ||
| 522 | |||
| 523 | |||
| 524 | class PartialPortMapperClient: | ||
| 525 | |||
| 526 | def addpackers(self): | ||
| 527 | self.packer = PortMapperPacker() | ||
| 528 | self.unpacker = PortMapperUnpacker('') | ||
| 529 | |||
| 530 | def Set(self, mapping): | ||
| 531 | return self.make_call(PMAPPROC_SET, mapping, \ | ||
| 532 | self.packer.pack_mapping, \ | ||
| 533 | self.unpacker.unpack_uint) | ||
| 534 | |||
| 535 | def Unset(self, mapping): | ||
| 536 | return self.make_call(PMAPPROC_UNSET, mapping, \ | ||
| 537 | self.packer.pack_mapping, \ | ||
| 538 | self.unpacker.unpack_uint) | ||
| 539 | |||
| 540 | def Getport(self, mapping): | ||
| 541 | return self.make_call(PMAPPROC_GETPORT, mapping, \ | ||
| 542 | self.packer.pack_mapping, \ | ||
| 543 | self.unpacker.unpack_uint) | ||
| 544 | |||
| 545 | def Dump(self): | ||
| 546 | return self.make_call(PMAPPROC_DUMP, None, \ | ||
| 547 | None, \ | ||
| 548 | self.unpacker.unpack_pmaplist) | ||
| 549 | |||
| 550 | def Callit(self, ca): | ||
| 551 | return self.make_call(PMAPPROC_CALLIT, ca, \ | ||
| 552 | self.packer.pack_call_args, \ | ||
| 553 | self.unpacker.unpack_call_result) | ||
| 554 | |||
| 555 | |||
| 556 | class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): | ||
| 557 | |||
| 558 | def __init__(self, host): | ||
| 559 | RawTCPClient.__init__(self, \ | ||
| 560 | host, PMAP_PROG, PMAP_VERS, PMAP_PORT) | ||
| 561 | |||
| 562 | |||
| 563 | class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient): | ||
| 564 | |||
| 565 | def __init__(self, host): | ||
| 566 | RawUDPClient.__init__(self, \ | ||
| 567 | host, PMAP_PROG, PMAP_VERS, PMAP_PORT) | ||
| 568 | |||
| 569 | |||
| 570 | class BroadcastUDPPortMapperClient(PartialPortMapperClient, \ | ||
| 571 | RawBroadcastUDPClient): | ||
| 572 | |||
| 573 | def __init__(self, bcastaddr): | ||
| 574 | RawBroadcastUDPClient.__init__(self, \ | ||
| 575 | bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT) | ||
| 576 | |||
| 577 | |||
| 578 | # Generic clients that find their server through the Port mapper | ||
| 579 | |||
| 580 | class TCPClient(RawTCPClient): | ||
| 581 | |||
| 582 | def __init__(self, host, prog, vers): | ||
| 583 | pmap = TCPPortMapperClient(host) | ||
| 584 | port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) | ||
| 585 | pmap.close() | ||
| 586 | if port == 0: | ||
| 587 | raise RuntimeError, 'program not registered' | ||
| 588 | RawTCPClient.__init__(self, host, prog, vers, port) | ||
| 589 | |||
| 590 | |||
| 591 | class UDPClient(RawUDPClient): | ||
| 592 | |||
| 593 | def __init__(self, host, prog, vers): | ||
| 594 | pmap = UDPPortMapperClient(host) | ||
| 595 | port = pmap.Getport((prog, vers, IPPROTO_UDP, 0)) | ||
| 596 | pmap.close() | ||
| 597 | if port == 0: | ||
| 598 | raise RuntimeError, 'program not registered' | ||
| 599 | RawUDPClient.__init__(self, host, prog, vers, port) | ||
| 600 | |||
| 601 | |||
| 602 | class BroadcastUDPClient(Client): | ||
| 603 | |||
| 604 | def __init__(self, bcastaddr, prog, vers): | ||
| 605 | self.pmap = BroadcastUDPPortMapperClient(bcastaddr) | ||
| 606 | self.pmap.set_reply_handler(self.my_reply_handler) | ||
| 607 | self.prog = prog | ||
| 608 | self.vers = vers | ||
| 609 | self.user_reply_handler = None | ||
| 610 | self.addpackers() | ||
| 611 | |||
| 612 | def close(self): | ||
| 613 | self.pmap.close() | ||
| 614 | |||
| 615 | def set_reply_handler(self, reply_handler): | ||
| 616 | self.user_reply_handler = reply_handler | ||
| 617 | |||
| 618 | def set_timeout(self, timeout): | ||
| 619 | self.pmap.set_timeout(timeout) | ||
| 620 | |||
| 621 | def my_reply_handler(self, reply, fromaddr): | ||
| 622 | port, res = reply | ||
| 623 | self.unpacker.reset(res) | ||
| 624 | result = self.unpack_func() | ||
| 625 | self.unpacker.done() | ||
| 626 | self.replies.append((result, fromaddr)) | ||
| 627 | if self.user_reply_handler is not None: | ||
| 628 | self.user_reply_handler(result, fromaddr) | ||
| 629 | |||
| 630 | def make_call(self, proc, args, pack_func, unpack_func): | ||
| 631 | self.packer.reset() | ||
| 632 | if pack_func: | ||
| 633 | pack_func(args) | ||
| 634 | if unpack_func is None: | ||
| 635 | def dummy(): pass | ||
| 636 | self.unpack_func = dummy | ||
| 637 | else: | ||
| 638 | self.unpack_func = unpack_func | ||
| 639 | self.replies = [] | ||
| 640 | packed_args = self.packer.get_buf() | ||
| 641 | dummy_replies = self.pmap.Callit( \ | ||
| 642 | (self.prog, self.vers, proc, packed_args)) | ||
| 643 | return self.replies | ||
| 644 | |||
| 645 | |||
| 646 | # Server classes | ||
| 647 | |||
| 648 | # These are not symmetric to the Client classes | ||
| 649 | # XXX No attempt is made to provide authorization hooks yet | ||
| 650 | |||
| 651 | class Server: | ||
| 652 | |||
| 653 | def __init__(self, host, prog, vers, port): | ||
| 654 | self.host = host # Should normally be '' for default interface | ||
| 655 | self.prog = prog | ||
| 656 | self.vers = vers | ||
| 657 | self.port = port # Should normally be 0 for random port | ||
| 658 | self.makesocket() # Assigns to self.sock and self.prot | ||
| 659 | self.bindsocket() | ||
| 660 | self.host, self.port = self.sock.getsockname() | ||
| 661 | self.addpackers() | ||
| 662 | |||
| 663 | def register(self): | ||
| 664 | mapping = self.prog, self.vers, self.prot, self.port | ||
| 665 | p = TCPPortMapperClient(self.host) | ||
| 666 | if not p.Set(mapping): | ||
| 667 | raise RuntimeError, 'register failed' | ||
| 668 | |||
| 669 | def unregister(self): | ||
| 670 | mapping = self.prog, self.vers, self.prot, self.port | ||
| 671 | p = TCPPortMapperClient(self.host) | ||
| 672 | if not p.Unset(mapping): | ||
| 673 | raise RuntimeError, 'unregister failed' | ||
| 674 | |||
| 675 | def handle(self, call): | ||
| 676 | # Don't use unpack_header but parse the header piecewise | ||
| 677 | # XXX I have no idea if I am using the right error responses! | ||
| 678 | self.unpacker.reset(call) | ||
| 679 | self.packer.reset() | ||
| 680 | xid = self.unpacker.unpack_uint() | ||
| 681 | self.packer.pack_uint(xid) | ||
| 682 | temp = self.unpacker.unpack_enum() | ||
| 683 | if temp != CALL: | ||
| 684 | return None # Not worthy of a reply | ||
| 685 | self.packer.pack_uint(REPLY) | ||
| 686 | temp = self.unpacker.unpack_uint() | ||
| 687 | if temp != RPCVERSION: | ||
| 688 | self.packer.pack_uint(MSG_DENIED) | ||
| 689 | self.packer.pack_uint(RPC_MISMATCH) | ||
| 690 | self.packer.pack_uint(RPCVERSION) | ||
| 691 | self.packer.pack_uint(RPCVERSION) | ||
| 692 | return self.packer.get_buf() | ||
| 693 | self.packer.pack_uint(MSG_ACCEPTED) | ||
| 694 | self.packer.pack_auth((AUTH_NULL, make_auth_null())) | ||
| 695 | prog = self.unpacker.unpack_uint() | ||
| 696 | if prog != self.prog: | ||
| 697 | self.packer.pack_uint(PROG_UNAVAIL) | ||
| 698 | return self.packer.get_buf() | ||
| 699 | vers = self.unpacker.unpack_uint() | ||
| 700 | if vers != self.vers: | ||
| 701 | self.packer.pack_uint(PROG_MISMATCH) | ||
| 702 | self.packer.pack_uint(self.vers) | ||
| 703 | self.packer.pack_uint(self.vers) | ||
| 704 | return self.packer.get_buf() | ||
| 705 | proc = self.unpacker.unpack_uint() | ||
| 706 | methname = 'handle_' + repr(proc) | ||
| 707 | try: | ||
| 708 | meth = getattr(self, methname) | ||
| 709 | except AttributeError: | ||
| 710 | self.packer.pack_uint(PROC_UNAVAIL) | ||
| 711 | return self.packer.get_buf() | ||
| 712 | cred = self.unpacker.unpack_auth() | ||
| 713 | verf = self.unpacker.unpack_auth() | ||
| 714 | try: | ||
| 715 | meth() # Unpack args, call turn_around(), pack reply | ||
| 716 | except (EOFError, GarbageArgs): | ||
| 717 | # Too few or too many arguments | ||
| 718 | self.packer.reset() | ||
| 719 | self.packer.pack_uint(xid) | ||
| 720 | self.packer.pack_uint(REPLY) | ||
| 721 | self.packer.pack_uint(MSG_ACCEPTED) | ||
| 722 | self.packer.pack_auth((AUTH_NULL, make_auth_null())) | ||
| 723 | self.packer.pack_uint(GARBAGE_ARGS) | ||
| 724 | return self.packer.get_buf() | ||
| 725 | |||
| 726 | def turn_around(self): | ||
| 727 | try: | ||
| 728 | self.unpacker.done() | ||
| 729 | except RuntimeError: | ||
| 730 | raise GarbageArgs | ||
| 731 | self.packer.pack_uint(SUCCESS) | ||
| 732 | |||
| 733 | def handle_0(self): # Handle NULL message | ||
| 734 | self.turn_around() | ||
| 735 | |||
| 736 | def makesocket(self): | ||
| 737 | # This MUST be overridden | ||
| 738 | raise RuntimeError, 'makesocket not defined' | ||
| 739 | |||
| 740 | def bindsocket(self): | ||
| 741 | # Override this to bind to a different port (e.g. reserved) | ||
| 742 | self.sock.bind((self.host, self.port)) | ||
| 743 | |||
| 744 | def addpackers(self): | ||
| 745 | # Override this to use derived classes from Packer/Unpacker | ||
| 746 | self.packer = Packer() | ||
| 747 | self.unpacker = Unpacker('') | ||
| 748 | |||
| 749 | |||
| 750 | class TCPServer(Server): | ||
| 751 | |||
| 752 | def makesocket(self): | ||
| 753 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 754 | self.prot = IPPROTO_TCP | ||
| 755 | |||
| 756 | def loop(self): | ||
| 757 | self.sock.listen(0) | ||
| 758 | while 1: | ||
| 759 | self.session(self.sock.accept()) | ||
| 760 | |||
| 761 | def session(self, connection): | ||
| 762 | sock, (host, port) = connection | ||
| 763 | while 1: | ||
| 764 | try: | ||
| 765 | call = recvrecord(sock) | ||
| 766 | except EOFError: | ||
| 767 | break | ||
| 768 | except socket.error, msg: | ||
| 769 | print 'socket error:', msg | ||
| 770 | break | ||
| 771 | reply = self.handle(call) | ||
| 772 | if reply is not None: | ||
| 773 | sendrecord(sock, reply) | ||
| 774 | |||
| 775 | def forkingloop(self): | ||
| 776 | # Like loop but uses forksession() | ||
| 777 | self.sock.listen(0) | ||
| 778 | while 1: | ||
| 779 | self.forksession(self.sock.accept()) | ||
| 780 | |||
| 781 | def forksession(self, connection): | ||
| 782 | # Like session but forks off a subprocess | ||
| 783 | import os | ||
| 784 | # Wait for deceased children | ||
| 785 | try: | ||
| 786 | while 1: | ||
| 787 | pid, sts = os.waitpid(0, 1) | ||
| 788 | except os.error: | ||
| 789 | pass | ||
| 790 | pid = None | ||
| 791 | try: | ||
| 792 | pid = os.fork() | ||
| 793 | if pid: # Parent | ||
| 794 | connection[0].close() | ||
| 795 | return | ||
| 796 | # Child | ||
| 797 | self.session(connection) | ||
| 798 | finally: | ||
| 799 | # Make sure we don't fall through in the parent | ||
| 800 | if pid == 0: | ||
| 801 | os._exit(0) | ||
| 802 | |||
| 803 | |||
| 804 | class UDPServer(Server): | ||
| 805 | |||
| 806 | def makesocket(self): | ||
| 807 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||
| 808 | self.prot = IPPROTO_UDP | ||
| 809 | |||
| 810 | def loop(self): | ||
| 811 | while 1: | ||
| 812 | self.session() | ||
| 813 | |||
| 814 | def session(self): | ||
| 815 | call, host_port = self.sock.recvfrom(8192) | ||
| 816 | reply = self.handle(call) | ||
| 817 | if reply != None: | ||
| 818 | self.sock.sendto(reply, host_port) | ||
| 819 | |||
| 820 | |||
| 821 | # Simple test program -- dump local portmapper status | ||
| 822 | |||
| 823 | def test(): | ||
| 824 | pmap = UDPPortMapperClient('') | ||
| 825 | list = pmap.Dump() | ||
| 826 | list.sort() | ||
| 827 | for prog, vers, prot, port in list: | ||
| 828 | print prog, vers, | ||
| 829 | if prot == IPPROTO_TCP: print 'tcp', | ||
| 830 | elif prot == IPPROTO_UDP: print 'udp', | ||
| 831 | else: print prot, | ||
| 832 | print port | ||
| 833 | |||
| 834 | |||
| 835 | # Test program for broadcast operation -- dump everybody's portmapper status | ||
| 836 | |||
| 837 | def testbcast(): | ||
| 838 | import sys | ||
| 839 | if sys.argv[1:]: | ||
| 840 | bcastaddr = sys.argv[1] | ||
| 841 | else: | ||
| 842 | bcastaddr = '<broadcast>' | ||
| 843 | def rh(reply, fromaddr): | ||
| 844 | host, port = fromaddr | ||
| 845 | print host + '\t' + repr(reply) | ||
| 846 | pmap = BroadcastUDPPortMapperClient(bcastaddr) | ||
| 847 | pmap.set_reply_handler(rh) | ||
| 848 | pmap.set_timeout(5) | ||
| 849 | replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0)) | ||
| 850 | |||
| 851 | |||
| 852 | # Test program for server, with corresponding client | ||
| 853 | # On machine A: python -c 'import rpc; rpc.testsvr()' | ||
| 854 | # On machine B: python -c 'import rpc; rpc.testclt()' A | ||
| 855 | # (A may be == B) | ||
| 856 | |||
| 857 | def testsvr(): | ||
| 858 | # Simple test class -- proc 1 doubles its string argument as reply | ||
| 859 | class S(UDPServer): | ||
| 860 | def handle_1(self): | ||
| 861 | arg = self.unpacker.unpack_string() | ||
| 862 | self.turn_around() | ||
| 863 | print 'RPC function 1 called, arg', repr(arg) | ||
| 864 | self.packer.pack_string(arg + arg) | ||
| 865 | # | ||
| 866 | s = S('', 0x20000000, 1, 0) | ||
| 867 | try: | ||
| 868 | s.unregister() | ||
| 869 | except RuntimeError, msg: | ||
| 870 | print 'RuntimeError:', msg, '(ignored)' | ||
| 871 | s.register() | ||
| 872 | print 'Service started...' | ||
| 873 | try: | ||
| 874 | s.loop() | ||
| 875 | finally: | ||
| 876 | s.unregister() | ||
| 877 | print 'Service interrupted.' | ||
| 878 | |||
| 879 | |||
| 880 | def testclt(): | ||
| 881 | import sys | ||
| 882 | if sys.argv[1:]: host = sys.argv[1] | ||
| 883 | else: host = '' | ||
| 884 | # Client for above server | ||
| 885 | class C(UDPClient): | ||
| 886 | def call_1(self, arg): | ||
| 887 | return self.make_call(1, arg, \ | ||
| 888 | self.packer.pack_string, \ | ||
| 889 | self.unpacker.unpack_string) | ||
| 890 | c = C(host, 0x20000000, 1) | ||
| 891 | print 'making call...' | ||
| 892 | reply = c.call_1('hello, world, ') | ||
| 893 | print 'call returned', repr(reply) | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | : ${PYTHON=python} | ||
| 2 | : ${SERVER=charon.cwi.nl} | ||
| 3 | |||
| 4 | set -xe | ||
| 5 | |||
| 6 | $PYTHON -c 'from rpc import test; test()' | ||
| 7 | $PYTHON -c 'from rpc import test; test()' ${SERVER} | ||
| 8 | |||
| 9 | $PYTHON -c 'from rpc import testsvr; testsvr()' & | ||
| 10 | PID=$! | ||
| 11 | sleep 2 | ||
| 12 | $PYTHON -c 'from rpc import testclt; testclt()' | ||
| 13 | kill -2 $PID | ||
| 14 | |||
| 15 | $PYTHON -c 'from mountclient import test; test()' | ||
| 16 | $PYTHON -c 'from mountclient import test; test()' gatekeeper.dec.com | ||
| 17 | |||
| 18 | $PYTHON -c 'from nfsclient import test; test()' | ||
| 19 | $PYTHON -c 'from nfsclient import test; test()' gatekeeper.dec.com | ||
| 20 | $PYTHON -c 'from nfsclient import test; test()' gatekeeper.dec.com /archive | ||
| 21 | |||
| 22 | $PYTHON -c 'from rnusersclient import test; test()' '' | ||
| 23 | |||
| 24 | $PYTHON -c 'from rpc import testbcast; testbcast()' | ||
| @@ -0,0 +1,200 @@ | |||
| 1 | # Implement (a subset of) Sun XDR -- RFC1014. | ||
| 2 | |||
| 3 | |||
| 4 | try: | ||
| 5 | import struct | ||
| 6 | except ImportError: | ||
| 7 | struct = None | ||
| 8 | |||
| 9 | |||
| 10 | Long = type(0L) | ||
| 11 | |||
| 12 | |||
| 13 | class Packer: | ||
| 14 | |||
| 15 | def __init__(self): | ||
| 16 | self.reset() | ||
| 17 | |||
| 18 | def reset(self): | ||
| 19 | self.buf = '' | ||
| 20 | |||
| 21 | def get_buf(self): | ||
| 22 | return self.buf | ||
| 23 | |||
| 24 | def pack_uint(self, x): | ||
| 25 | self.buf = self.buf + \ | ||
| 26 | (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \ | ||
| 27 | chr(int(x>>8 & 0xff)) + chr(int(x & 0xff))) | ||
| 28 | if struct and struct.pack('l', 1) == '\0\0\0\1': | ||
| 29 | def pack_uint(self, x): | ||
| 30 | if type(x) == Long: | ||
| 31 | x = int((x + 0x80000000L) % 0x100000000L \ | ||
| 32 | - 0x80000000L) | ||
| 33 | self.buf = self.buf + struct.pack('l', x) | ||
| 34 | |||
| 35 | pack_int = pack_uint | ||
| 36 | |||
| 37 | pack_enum = pack_int | ||
| 38 | |||
| 39 | def pack_bool(self, x): | ||
| 40 | if x: self.buf = self.buf + '\0\0\0\1' | ||
| 41 | else: self.buf = self.buf + '\0\0\0\0' | ||
| 42 | |||
| 43 | def pack_uhyper(self, x): | ||
| 44 | self.pack_uint(int(x>>32 & 0xffffffff)) | ||
| 45 | self.pack_uint(int(x & 0xffffffff)) | ||
| 46 | |||
| 47 | pack_hyper = pack_uhyper | ||
| 48 | |||
| 49 | def pack_float(self, x): | ||
| 50 | # XXX | ||
| 51 | self.buf = self.buf + struct.pack('f', x) | ||
| 52 | |||
| 53 | def pack_double(self, x): | ||
| 54 | # XXX | ||
| 55 | self.buf = self.buf + struct.pack('d', x) | ||
| 56 | |||
| 57 | def pack_fstring(self, n, s): | ||
| 58 | if n < 0: | ||
| 59 | raise ValueError, 'fstring size must be nonnegative' | ||
| 60 | n = ((n+3)/4)*4 | ||
| 61 | data = s[:n] | ||
| 62 | data = data + (n - len(data)) * '\0' | ||
| 63 | self.buf = self.buf + data | ||
| 64 | |||
| 65 | pack_fopaque = pack_fstring | ||
| 66 | |||
| 67 | def pack_string(self, s): | ||
| 68 | n = len(s) | ||
| 69 | self.pack_uint(n) | ||
| 70 | self.pack_fstring(n, s) | ||
| 71 | |||
| 72 | pack_opaque = pack_string | ||
| 73 | |||
| 74 | def pack_list(self, list, pack_item): | ||
| 75 | for item in list: | ||
| 76 | self.pack_uint(1) | ||
| 77 | pack_item(item) | ||
| 78 | self.pack_uint(0) | ||
| 79 | |||
| 80 | def pack_farray(self, n, list, pack_item): | ||
| 81 | if len(list) <> n: | ||
| 82 | raise ValueError, 'wrong array size' | ||
| 83 | for item in list: | ||
| 84 | pack_item(item) | ||
| 85 | |||
| 86 | def pack_array(self, list, pack_item): | ||
| 87 | n = len(list) | ||
| 88 | self.pack_uint(n) | ||
| 89 | self.pack_farray(n, list, pack_item) | ||
| 90 | |||
| 91 | |||
| 92 | class Unpacker: | ||
| 93 | |||
| 94 | def __init__(self, data): | ||
| 95 | self.reset(data) | ||
| 96 | |||
| 97 | def reset(self, data): | ||
| 98 | self.buf = data | ||
| 99 | self.pos = 0 | ||
| 100 | |||
| 101 | def done(self): | ||
| 102 | if self.pos < len(self.buf): | ||
| 103 | raise RuntimeError, 'unextracted data remains' | ||
| 104 | |||
| 105 | def unpack_uint(self): | ||
| 106 | i = self.pos | ||
| 107 | self.pos = j = i+4 | ||
| 108 | data = self.buf[i:j] | ||
| 109 | if len(data) < 4: | ||
| 110 | raise EOFError | ||
| 111 | x = long(ord(data[0]))<<24 | ord(data[1])<<16 | \ | ||
| 112 | ord(data[2])<<8 | ord(data[3]) | ||
| 113 | # Return a Python long only if the value is not representable | ||
| 114 | # as a nonnegative Python int | ||
| 115 | if x < 0x80000000L: x = int(x) | ||
| 116 | return x | ||
| 117 | if struct and struct.unpack('l', '\0\0\0\1') == 1: | ||
| 118 | def unpack_uint(self): | ||
| 119 | i = self.pos | ||
| 120 | self.pos = j = i+4 | ||
| 121 | data = self.buf[i:j] | ||
| 122 | if len(data) < 4: | ||
| 123 | raise EOFError | ||
| 124 | return struct.unpack('l', data) | ||
| 125 | |||
| 126 | def unpack_int(self): | ||
| 127 | x = self.unpack_uint() | ||
| 128 | if x >= 0x80000000L: x = x - 0x100000000L | ||
| 129 | return int(x) | ||
| 130 | |||
| 131 | unpack_enum = unpack_int | ||
| 132 | |||
| 133 | unpack_bool = unpack_int | ||
| 134 | |||
| 135 | def unpack_uhyper(self): | ||
| 136 | hi = self.unpack_uint() | ||
| 137 | lo = self.unpack_uint() | ||
| 138 | return long(hi)<<32 | lo | ||
| 139 | |||
| 140 | def unpack_hyper(self): | ||
| 141 | x = self.unpack_uhyper() | ||
| 142 | if x >= 0x8000000000000000L: x = x - 0x10000000000000000L | ||
| 143 | return x | ||
| 144 | |||
| 145 | def unpack_float(self): | ||
| 146 | # XXX | ||
| 147 | i = self.pos | ||
| 148 | self.pos = j = i+4 | ||
| 149 | data = self.buf[i:j] | ||
| 150 | if len(data) < 4: | ||
| 151 | raise EOFError | ||
| 152 | return struct.unpack('f', data)[0] | ||
| 153 | |||
| 154 | def unpack_double(self): | ||
| 155 | # XXX | ||
| 156 | i = self.pos | ||
| 157 | self.pos = j = i+8 | ||
| 158 | data = self.buf[i:j] | ||
| 159 | if len(data) < 8: | ||
| 160 | raise EOFError | ||
| 161 | return struct.unpack('d', data)[0] | ||
| 162 | |||
| 163 | def unpack_fstring(self, n): | ||
| 164 | if n < 0: | ||
| 165 | raise ValueError, 'fstring size must be nonnegative' | ||
| 166 | i = self.pos | ||
| 167 | j = i + (n+3)/4*4 | ||
| 168 | if j > len(self.buf): | ||
| 169 | raise EOFError | ||
| 170 | self.pos = j | ||
| 171 | return self.buf[i:i+n] | ||
| 172 | |||
| 173 | unpack_fopaque = unpack_fstring | ||
| 174 | |||
| 175 | def unpack_string(self): | ||
| 176 | n = self.unpack_uint() | ||
| 177 | return self.unpack_fstring(n) | ||
| 178 | |||
| 179 | unpack_opaque = unpack_string | ||
| 180 | |||
| 181 | def unpack_list(self, unpack_item): | ||
| 182 | list = [] | ||
| 183 | while 1: | ||
| 184 | x = self.unpack_uint() | ||
| 185 | if x == 0: break | ||
| 186 | if x <> 1: | ||
| 187 | raise RuntimeError, '0 or 1 expected, got %r' % (x, ) | ||
| 188 | item = unpack_item() | ||
| 189 | list.append(item) | ||
| 190 | return list | ||
| 191 | |||
| 192 | def unpack_farray(self, n, unpack_item): | ||
| 193 | list = [] | ||
| 194 | for i in range(n): | ||
| 195 | list.append(unpack_item()) | ||
| 196 | return list | ||
| 197 | |||
| 198 | def unpack_array(self, unpack_item): | ||
| 199 | n = self.unpack_uint() | ||
| 200 | return self.unpack_farray(n, unpack_item) | ||
| @@ -0,0 +1,23 @@ | |||
| 1 | This directory contains a collection of executable Python scripts. | ||
| 2 | |||
| 3 | See also the Tools/scripts directory! | ||
| 4 | |||
| 5 | beer.py Print the classic 'bottles of beer' list. | ||
| 6 | eqfix.py Fix .py files to use the correct equality test operator | ||
| 7 | fact.py Factorize numbers | ||
| 8 | find-uname.py Search for Unicode characters using regexps. | ||
| 9 | from.py Summarize mailbox | ||
| 10 | ftpstats.py Summarize ftp daemon log file | ||
| 11 | lpwatch.py Watch BSD line printer queues | ||
| 12 | makedir.py Like mkdir -p | ||
| 13 | markov.py Markov chain simulation of words or characters | ||
| 14 | mboxconvvert.py Convert MH or MMDF mailboxes to unix mailbox format | ||
| 15 | mkrcs.py Fix symlinks named RCS into parallel tree | ||
| 16 | morse.py Produce morse code (audible or on AIFF file) | ||
| 17 | pi.py Print all digits of pi -- given enough time and memory | ||
| 18 | pp.py Emulate some Perl command line options | ||
| 19 | primes.py Print prime numbers | ||
| 20 | queens.py Dijkstra's solution to Wirth's "N Queens problem" | ||
| 21 | script.py Equivalent to BSD script(1) -- by Steen Lumholt | ||
| 22 | unbirthday.py Print unbirthday count | ||
| 23 | update.py Update a bunch of files according to a script. | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | # By GvR, demystified after a version by Fredrik Lundh. | ||
| 3 | import sys | ||
| 4 | n = 100 | ||
| 5 | if sys.argv[1:]: n = int(sys.argv[1]) | ||
| 6 | def bottle(n): | ||
| 7 | if n == 0: return "no more bottles of beer" | ||
| 8 | if n == 1: return "one bottle of beer" | ||
| 9 | return str(n) + " bottles of beer" | ||
| 10 | for i in range(n): | ||
| 11 | print bottle(n-i), "on the wall," | ||
| 12 | print bottle(n-i) + "." | ||
| 13 | print "Take one down, pass it around," | ||
| 14 | print bottle(n-i-1), "on the wall." | ||
| @@ -0,0 +1,198 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Fix Python source files to use the new equality test operator, i.e., | ||
| 4 | # if x = y: ... | ||
| 5 | # is changed to | ||
| 6 | # if x == y: ... | ||
| 7 | # The script correctly tokenizes the Python program to reliably | ||
| 8 | # distinguish between assignments and equality tests. | ||
| 9 | # | ||
| 10 | # Command line arguments are files or directories to be processed. | ||
| 11 | # Directories are searched recursively for files whose name looks | ||
| 12 | # like a python module. | ||
| 13 | # Symbolic links are always ignored (except as explicit directory | ||
| 14 | # arguments). Of course, the original file is kept as a back-up | ||
| 15 | # (with a "~" attached to its name). | ||
| 16 | # It complains about binaries (files containing null bytes) | ||
| 17 | # and about files that are ostensibly not Python files: if the first | ||
| 18 | # line starts with '#!' and does not contain the string 'python'. | ||
| 19 | # | ||
| 20 | # Changes made are reported to stdout in a diff-like format. | ||
| 21 | # | ||
| 22 | # Undoubtedly you can do this using find and sed or perl, but this is | ||
| 23 | # a nice example of Python code that recurses down a directory tree | ||
| 24 | # and uses regular expressions. Also note several subtleties like | ||
| 25 | # preserving the file's mode and avoiding to even write a temp file | ||
| 26 | # when no changes are needed for a file. | ||
| 27 | # | ||
| 28 | # NB: by changing only the function fixline() you can turn this | ||
| 29 | # into a program for a different change to Python programs... | ||
| 30 | |||
| 31 | import sys | ||
| 32 | import re | ||
| 33 | import os | ||
| 34 | from stat import * | ||
| 35 | import string | ||
| 36 | |||
| 37 | err = sys.stderr.write | ||
| 38 | dbg = err | ||
| 39 | rep = sys.stdout.write | ||
| 40 | |||
| 41 | def main(): | ||
| 42 | bad = 0 | ||
| 43 | if not sys.argv[1:]: # No arguments | ||
| 44 | err('usage: ' + sys.argv[0] + ' file-or-directory ...\n') | ||
| 45 | sys.exit(2) | ||
| 46 | for arg in sys.argv[1:]: | ||
| 47 | if os.path.isdir(arg): | ||
| 48 | if recursedown(arg): bad = 1 | ||
| 49 | elif os.path.islink(arg): | ||
| 50 | err(arg + ': will not process symbolic links\n') | ||
| 51 | bad = 1 | ||
| 52 | else: | ||
| 53 | if fix(arg): bad = 1 | ||
| 54 | sys.exit(bad) | ||
| 55 | |||
| 56 | ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') | ||
| 57 | def ispython(name): | ||
| 58 | return ispythonprog.match(name) >= 0 | ||
| 59 | |||
| 60 | def recursedown(dirname): | ||
| 61 | dbg('recursedown(%r)\n' % (dirname,)) | ||
| 62 | bad = 0 | ||
| 63 | try: | ||
| 64 | names = os.listdir(dirname) | ||
| 65 | except os.error, msg: | ||
| 66 | err('%s: cannot list directory: %r\n' % (dirname, msg)) | ||
| 67 | return 1 | ||
| 68 | names.sort() | ||
| 69 | subdirs = [] | ||
| 70 | for name in names: | ||
| 71 | if name in (os.curdir, os.pardir): continue | ||
| 72 | fullname = os.path.join(dirname, name) | ||
| 73 | if os.path.islink(fullname): pass | ||
| 74 | elif os.path.isdir(fullname): | ||
| 75 | subdirs.append(fullname) | ||
| 76 | elif ispython(name): | ||
| 77 | if fix(fullname): bad = 1 | ||
| 78 | for fullname in subdirs: | ||
| 79 | if recursedown(fullname): bad = 1 | ||
| 80 | return bad | ||
| 81 | |||
| 82 | def fix(filename): | ||
| 83 | ## dbg('fix(%r)\n' % (dirname,)) | ||
| 84 | try: | ||
| 85 | f = open(filename, 'r') | ||
| 86 | except IOError, msg: | ||
| 87 | err('%s: cannot open: %r\n' % (filename, msg)) | ||
| 88 | return 1 | ||
| 89 | head, tail = os.path.split(filename) | ||
| 90 | tempname = os.path.join(head, '@' + tail) | ||
| 91 | g = None | ||
| 92 | # If we find a match, we rewind the file and start over but | ||
| 93 | # now copy everything to a temp file. | ||
| 94 | lineno = 0 | ||
| 95 | while 1: | ||
| 96 | line = f.readline() | ||
| 97 | if not line: break | ||
| 98 | lineno = lineno + 1 | ||
| 99 | if g is None and '\0' in line: | ||
| 100 | # Check for binary files | ||
| 101 | err(filename + ': contains null bytes; not fixed\n') | ||
| 102 | f.close() | ||
| 103 | return 1 | ||
| 104 | if lineno == 1 and g is None and line[:2] == '#!': | ||
| 105 | # Check for non-Python scripts | ||
| 106 | words = string.split(line[2:]) | ||
| 107 | if words and re.search('[pP]ython', words[0]) < 0: | ||
| 108 | msg = filename + ': ' + words[0] | ||
| 109 | msg = msg + ' script; not fixed\n' | ||
| 110 | err(msg) | ||
| 111 | f.close() | ||
| 112 | return 1 | ||
| 113 | while line[-2:] == '\\\n': | ||
| 114 | nextline = f.readline() | ||
| 115 | if not nextline: break | ||
| 116 | line = line + nextline | ||
| 117 | lineno = lineno + 1 | ||
| 118 | newline = fixline(line) | ||
| 119 | if newline != line: | ||
| 120 | if g is None: | ||
| 121 | try: | ||
| 122 | g = open(tempname, 'w') | ||
| 123 | except IOError, msg: | ||
| 124 | f.close() | ||
| 125 | err('%s: cannot create: %r\n' % (tempname, msg)) | ||
| 126 | return 1 | ||
| 127 | f.seek(0) | ||
| 128 | lineno = 0 | ||
| 129 | rep(filename + ':\n') | ||
| 130 | continue # restart from the beginning | ||
| 131 | rep(repr(lineno) + '\n') | ||
| 132 | rep('< ' + line) | ||
| 133 | rep('> ' + newline) | ||
| 134 | if g is not None: | ||
| 135 | g.write(newline) | ||
| 136 | |||
| 137 | # End of file | ||
| 138 | f.close() | ||
| 139 | if not g: return 0 # No changes | ||
| 140 | |||
| 141 | # Finishing touch -- move files | ||
| 142 | |||
| 143 | # First copy the file's mode to the temp file | ||
| 144 | try: | ||
| 145 | statbuf = os.stat(filename) | ||
| 146 | os.chmod(tempname, statbuf[ST_MODE] & 07777) | ||
| 147 | except os.error, msg: | ||
| 148 | err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) | ||
| 149 | # Then make a backup of the original file as filename~ | ||
| 150 | try: | ||
| 151 | os.rename(filename, filename + '~') | ||
| 152 | except os.error, msg: | ||
| 153 | err('%s: warning: backup failed (%r)\n' % (filename, msg)) | ||
| 154 | # Now move the temp file to the original file | ||
| 155 | try: | ||
| 156 | os.rename(tempname, filename) | ||
| 157 | except os.error, msg: | ||
| 158 | err('%s: rename failed (%r)\n' % (filename, msg)) | ||
| 159 | return 1 | ||
| 160 | # Return succes | ||
| 161 | return 0 | ||
| 162 | |||
| 163 | |||
| 164 | from tokenize import tokenprog | ||
| 165 | |||
| 166 | match = {'if':':', 'elif':':', 'while':':', 'return':'\n', \ | ||
| 167 | '(':')', '[':']', '{':'}', '`':'`'} | ||
| 168 | |||
| 169 | def fixline(line): | ||
| 170 | # Quick check for easy case | ||
| 171 | if '=' not in line: return line | ||
| 172 | |||
| 173 | i, n = 0, len(line) | ||
| 174 | stack = [] | ||
| 175 | while i < n: | ||
| 176 | j = tokenprog.match(line, i) | ||
| 177 | if j < 0: | ||
| 178 | # A bad token; forget about the rest of this line | ||
| 179 | print '(Syntax error:)' | ||
| 180 | print line, | ||
| 181 | return line | ||
| 182 | a, b = tokenprog.regs[3] # Location of the token proper | ||
| 183 | token = line[a:b] | ||
| 184 | i = i+j | ||
| 185 | if stack and token == stack[-1]: | ||
| 186 | del stack[-1] | ||
| 187 | elif match.has_key(token): | ||
| 188 | stack.append(match[token]) | ||
| 189 | elif token == '=' and stack: | ||
| 190 | line = line[:a] + '==' + line[b:] | ||
| 191 | i, n = a + len('=='), len(line) | ||
| 192 | elif token == '==' and not stack: | ||
| 193 | print '(Warning: \'==\' at top level:)' | ||
| 194 | print line, | ||
| 195 | return line | ||
| 196 | |||
| 197 | if __name__ == "__main__": | ||
| 198 | main() | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Factorize numbers. | ||
| 4 | # The algorithm is not efficient, but easy to understand. | ||
| 5 | # If there are large factors, it will take forever to find them, | ||
| 6 | # because we try all odd numbers between 3 and sqrt(n)... | ||
| 7 | |||
| 8 | import sys | ||
| 9 | from math import sqrt | ||
| 10 | |||
| 11 | error = 'fact.error' # exception | ||
| 12 | |||
| 13 | def fact(n): | ||
| 14 | if n < 1: raise error # fact() argument should be >= 1 | ||
| 15 | if n == 1: return [] # special case | ||
| 16 | res = [] | ||
| 17 | # Treat even factors special, so we can use i = i+2 later | ||
| 18 | while n%2 == 0: | ||
| 19 | res.append(2) | ||
| 20 | n = n/2 | ||
| 21 | # Try odd numbers up to sqrt(n) | ||
| 22 | limit = sqrt(float(n+1)) | ||
| 23 | i = 3 | ||
| 24 | while i <= limit: | ||
| 25 | if n%i == 0: | ||
| 26 | res.append(i) | ||
| 27 | n = n/i | ||
| 28 | limit = sqrt(n+1) | ||
| 29 | else: | ||
| 30 | i = i+2 | ||
| 31 | if n != 1: | ||
| 32 | res.append(n) | ||
| 33 | return res | ||
| 34 | |||
| 35 | def main(): | ||
| 36 | if len(sys.argv) > 1: | ||
| 37 | for arg in sys.argv[1:]: | ||
| 38 | n = eval(arg) | ||
| 39 | print n, fact(n) | ||
| 40 | else: | ||
| 41 | try: | ||
| 42 | while 1: | ||
| 43 | n = input() | ||
| 44 | print n, fact(n) | ||
| 45 | except EOFError: | ||
| 46 | pass | ||
| 47 | |||
| 48 | if __name__ == "__main__": | ||
| 49 | main() | ||
| @@ -0,0 +1,40 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | """ | ||
| 4 | For each argument on the command line, look for it in the set of all Unicode | ||
| 5 | names. Arguments are treated as case-insensitive regular expressions, e.g.: | ||
| 6 | |||
| 7 | % find-uname 'small letter a$' 'horizontal line' | ||
| 8 | *** small letter a$ matches *** | ||
| 9 | LATIN SMALL LETTER A (97) | ||
| 10 | COMBINING LATIN SMALL LETTER A (867) | ||
| 11 | CYRILLIC SMALL LETTER A (1072) | ||
| 12 | PARENTHESIZED LATIN SMALL LETTER A (9372) | ||
| 13 | CIRCLED LATIN SMALL LETTER A (9424) | ||
| 14 | FULLWIDTH LATIN SMALL LETTER A (65345) | ||
| 15 | *** horizontal line matches *** | ||
| 16 | HORIZONTAL LINE EXTENSION (9135) | ||
| 17 | """ | ||
| 18 | |||
| 19 | import unicodedata | ||
| 20 | import sys | ||
| 21 | import re | ||
| 22 | |||
| 23 | def main(args): | ||
| 24 | unicode_names= [] | ||
| 25 | for ix in range(sys.maxunicode+1): | ||
| 26 | try: | ||
| 27 | unicode_names.append( (ix, unicodedata.name(unichr(ix))) ) | ||
| 28 | except ValueError: # no name for the character | ||
| 29 | pass | ||
| 30 | for arg in args: | ||
| 31 | pat = re.compile(arg, re.I) | ||
| 32 | matches = [(x,y) for (x,y) in unicode_names | ||
| 33 | if pat.search(y) is not None] | ||
| 34 | if matches: | ||
| 35 | print "***", arg, "matches", "***" | ||
| 36 | for (x,y) in matches: | ||
| 37 | print "%s (%d)" % (y,x) | ||
| 38 | |||
| 39 | if __name__ == "__main__": | ||
| 40 | main(sys.argv[1:]) | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Print From and Subject of messages in $MAIL. | ||
| 4 | # Extension to multiple mailboxes and other bells & whistles are left | ||
| 5 | # as exercises for the reader. | ||
| 6 | |||
| 7 | import sys, os | ||
| 8 | |||
| 9 | # Open mailbox file. Exits with exception when this fails. | ||
| 10 | |||
| 11 | try: | ||
| 12 | mailbox = os.environ['MAIL'] | ||
| 13 | except (AttributeError, KeyError): | ||
| 14 | sys.stderr.write('No environment variable $MAIL\n') | ||
| 15 | sys.exit(2) | ||
| 16 | |||
| 17 | try: | ||
| 18 | mail = open(mailbox) | ||
| 19 | except IOError: | ||
| 20 | sys.exit('Cannot open mailbox file: ' + mailbox) | ||
| 21 | |||
| 22 | while 1: | ||
| 23 | line = mail.readline() | ||
| 24 | if not line: | ||
| 25 | break # EOF | ||
| 26 | if line.startswith('From '): | ||
| 27 | # Start of message found | ||
| 28 | print line[:-1], | ||
| 29 | while 1: | ||
| 30 | line = mail.readline() | ||
| 31 | if not line or line == '\n': | ||
| 32 | break | ||
| 33 | if line.startswith('Subject: '): | ||
| 34 | print repr(line[9:-1]), | ||
| 35 | |||
| @@ -0,0 +1,145 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Extract statistics from ftp daemon log. | ||
| 4 | |||
| 5 | # Usage: | ||
| 6 | # ftpstats [-m maxitems] [-s search] [file] | ||
| 7 | # -m maxitems: restrict number of items in "top-N" lists, default 25. | ||
| 8 | # -s string: restrict statistics to lines containing this string. | ||
| 9 | # Default file is /usr/adm/ftpd; a "-" means read standard input. | ||
| 10 | |||
| 11 | # The script must be run on the host where the ftp daemon runs. | ||
| 12 | # (At CWI this is currently buizerd.) | ||
| 13 | |||
| 14 | import os | ||
| 15 | import sys | ||
| 16 | import re | ||
| 17 | import string | ||
| 18 | import getopt | ||
| 19 | |||
| 20 | pat = '^([a-zA-Z0-9 :]*)!(.*)!(.*)!([<>].*)!([0-9]+)!([0-9]+)$' | ||
| 21 | prog = re.compile(pat) | ||
| 22 | |||
| 23 | def main(): | ||
| 24 | maxitems = 25 | ||
| 25 | search = None | ||
| 26 | try: | ||
| 27 | opts, args = getopt.getopt(sys.argv[1:], 'm:s:') | ||
| 28 | except getopt.error, msg: | ||
| 29 | print msg | ||
| 30 | print 'usage: ftpstats [-m maxitems] [file]' | ||
| 31 | sys.exit(2) | ||
| 32 | for o, a in opts: | ||
| 33 | if o == '-m': | ||
| 34 | maxitems = string.atoi(a) | ||
| 35 | if o == '-s': | ||
| 36 | search = a | ||
| 37 | file = '/usr/adm/ftpd' | ||
| 38 | if args: file = args[0] | ||
| 39 | if file == '-': | ||
| 40 | f = sys.stdin | ||
| 41 | else: | ||
| 42 | try: | ||
| 43 | f = open(file, 'r') | ||
| 44 | except IOError, msg: | ||
| 45 | print file, ':', msg | ||
| 46 | sys.exit(1) | ||
| 47 | bydate = {} | ||
| 48 | bytime = {} | ||
| 49 | byfile = {} | ||
| 50 | bydir = {} | ||
| 51 | byhost = {} | ||
| 52 | byuser = {} | ||
| 53 | bytype = {} | ||
| 54 | lineno = 0 | ||
| 55 | try: | ||
| 56 | while 1: | ||
| 57 | line = f.readline() | ||
| 58 | if not line: break | ||
| 59 | lineno = lineno + 1 | ||
| 60 | if search and string.find(line, search) < 0: | ||
| 61 | continue | ||
| 62 | if prog.match(line) < 0: | ||
| 63 | print 'Bad line', lineno, ':', repr(line) | ||
| 64 | continue | ||
| 65 | items = prog.group(1, 2, 3, 4, 5, 6) | ||
| 66 | (logtime, loguser, loghost, logfile, logbytes, | ||
| 67 | logxxx2) = items | ||
| 68 | ## print logtime | ||
| 69 | ## print '-->', loguser | ||
| 70 | ## print '--> -->', loghost | ||
| 71 | ## print '--> --> -->', logfile | ||
| 72 | ## print '--> --> --> -->', logbytes | ||
| 73 | ## print '--> --> --> --> -->', logxxx2 | ||
| 74 | ## for i in logtime, loghost, logbytes, logxxx2: | ||
| 75 | ## if '!' in i: print '???', i | ||
| 76 | add(bydate, logtime[-4:] + ' ' + logtime[:6], items) | ||
| 77 | add(bytime, logtime[7:9] + ':00-59', items) | ||
| 78 | direction, logfile = logfile[0], logfile[1:] | ||
| 79 | # The real path probably starts at the last //... | ||
| 80 | while 1: | ||
| 81 | i = string.find(logfile, '//') | ||
| 82 | if i < 0: break | ||
| 83 | logfile = logfile[i+1:] | ||
| 84 | add(byfile, logfile + ' ' + direction, items) | ||
| 85 | logdir = os.path.dirname(logfile) | ||
| 86 | ## logdir = os.path.normpath(logdir) + '/.' | ||
| 87 | while 1: | ||
| 88 | add(bydir, logdir + ' ' + direction, items) | ||
| 89 | dirhead = os.path.dirname(logdir) | ||
| 90 | if dirhead == logdir: break | ||
| 91 | logdir = dirhead | ||
| 92 | add(byhost, loghost, items) | ||
| 93 | add(byuser, loguser, items) | ||
| 94 | add(bytype, direction, items) | ||
| 95 | except KeyboardInterrupt: | ||
| 96 | print 'Interrupted at line', lineno | ||
| 97 | show(bytype, 'by transfer direction', maxitems) | ||
| 98 | show(bydir, 'by directory', maxitems) | ||
| 99 | show(byfile, 'by file', maxitems) | ||
| 100 | show(byhost, 'by host', maxitems) | ||
| 101 | show(byuser, 'by user', maxitems) | ||
| 102 | showbar(bydate, 'by date') | ||
| 103 | showbar(bytime, 'by time of day') | ||
| 104 | |||
| 105 | def showbar(dict, title): | ||
| 106 | n = len(title) | ||
| 107 | print '='*((70-n)/2), title, '='*((71-n)/2) | ||
| 108 | list = [] | ||
| 109 | keys = dict.keys() | ||
| 110 | keys.sort() | ||
| 111 | for key in keys: | ||
| 112 | n = len(str(key)) | ||
| 113 | list.append((len(dict[key]), key)) | ||
| 114 | maxkeylength = 0 | ||
| 115 | maxcount = 0 | ||
| 116 | for count, key in list: | ||
| 117 | maxkeylength = max(maxkeylength, len(key)) | ||
| 118 | maxcount = max(maxcount, count) | ||
| 119 | maxbarlength = 72 - maxkeylength - 7 | ||
| 120 | for count, key in list: | ||
| 121 | barlength = int(round(maxbarlength*float(count)/maxcount)) | ||
| 122 | bar = '*'*barlength | ||
| 123 | print '%5d %-*s %s' % (count, maxkeylength, key, bar) | ||
| 124 | |||
| 125 | def show(dict, title, maxitems): | ||
| 126 | if len(dict) > maxitems: | ||
| 127 | title = title + ' (first %d)'%maxitems | ||
| 128 | n = len(title) | ||
| 129 | print '='*((70-n)/2), title, '='*((71-n)/2) | ||
| 130 | list = [] | ||
| 131 | keys = dict.keys() | ||
| 132 | for key in keys: | ||
| 133 | list.append((-len(dict[key]), key)) | ||
| 134 | list.sort() | ||
| 135 | for count, key in list[:maxitems]: | ||
| 136 | print '%5d %s' % (-count, key) | ||
| 137 | |||
| 138 | def add(dict, key, item): | ||
| 139 | if dict.has_key(key): | ||
| 140 | dict[key].append(item) | ||
| 141 | else: | ||
| 142 | dict[key] = [item] | ||
| 143 | |||
| 144 | if __name__ == "__main__": | ||
| 145 | main() | ||
| @@ -0,0 +1,110 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Watch line printer queue(s). | ||
| 4 | # Intended for BSD 4.3 lpq. | ||
| 5 | |||
| 6 | import posix | ||
| 7 | import sys | ||
| 8 | import time | ||
| 9 | import string | ||
| 10 | |||
| 11 | DEF_PRINTER = 'psc' | ||
| 12 | DEF_DELAY = 10 | ||
| 13 | |||
| 14 | def main(): | ||
| 15 | delay = DEF_DELAY # XXX Use getopt() later | ||
| 16 | try: | ||
| 17 | thisuser = posix.environ['LOGNAME'] | ||
| 18 | except: | ||
| 19 | thisuser = posix.environ['USER'] | ||
| 20 | printers = sys.argv[1:] | ||
| 21 | if printers: | ||
| 22 | # Strip '-P' from printer names just in case | ||
| 23 | # the user specified it... | ||
| 24 | for i in range(len(printers)): | ||
| 25 | if printers[i][:2] == '-P': | ||
| 26 | printers[i] = printers[i][2:] | ||
| 27 | else: | ||
| 28 | if posix.environ.has_key('PRINTER'): | ||
| 29 | printers = [posix.environ['PRINTER']] | ||
| 30 | else: | ||
| 31 | printers = [DEF_PRINTER] | ||
| 32 | # | ||
| 33 | clearhome = posix.popen('clear', 'r').read() | ||
| 34 | # | ||
| 35 | while 1: | ||
| 36 | text = clearhome | ||
| 37 | for name in printers: | ||
| 38 | text = text + makestatus(name, thisuser) + '\n' | ||
| 39 | print text | ||
| 40 | time.sleep(delay) | ||
| 41 | |||
| 42 | def makestatus(name, thisuser): | ||
| 43 | pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r') | ||
| 44 | lines = [] | ||
| 45 | users = {} | ||
| 46 | aheadbytes = 0 | ||
| 47 | aheadjobs = 0 | ||
| 48 | userseen = 0 | ||
| 49 | totalbytes = 0 | ||
| 50 | totaljobs = 0 | ||
| 51 | while 1: | ||
| 52 | line = pipe.readline() | ||
| 53 | if not line: break | ||
| 54 | fields = string.split(line) | ||
| 55 | n = len(fields) | ||
| 56 | if len(fields) >= 6 and fields[n-1] == 'bytes': | ||
| 57 | rank = fields[0] | ||
| 58 | user = fields[1] | ||
| 59 | job = fields[2] | ||
| 60 | files = fields[3:-2] | ||
| 61 | bytes = eval(fields[n-2]) | ||
| 62 | if user == thisuser: | ||
| 63 | userseen = 1 | ||
| 64 | elif not userseen: | ||
| 65 | aheadbytes = aheadbytes + bytes | ||
| 66 | aheadjobs = aheadjobs + 1 | ||
| 67 | totalbytes = totalbytes + bytes | ||
| 68 | totaljobs = totaljobs + 1 | ||
| 69 | if users.has_key(user): | ||
| 70 | ujobs, ubytes = users[user] | ||
| 71 | else: | ||
| 72 | ujobs, ubytes = 0, 0 | ||
| 73 | ujobs = ujobs + 1 | ||
| 74 | ubytes = ubytes + bytes | ||
| 75 | users[user] = ujobs, ubytes | ||
| 76 | else: | ||
| 77 | if fields and fields[0] <> 'Rank': | ||
| 78 | line = string.strip(line) | ||
| 79 | if line == 'no entries': | ||
| 80 | line = name + ': idle' | ||
| 81 | elif line[-22:] == ' is ready and printing': | ||
| 82 | line = name | ||
| 83 | lines.append(line) | ||
| 84 | # | ||
| 85 | if totaljobs: | ||
| 86 | line = '%d K' % ((totalbytes+1023)/1024) | ||
| 87 | if totaljobs <> len(users): | ||
| 88 | line = line + ' (%d jobs)' % totaljobs | ||
| 89 | if len(users) == 1: | ||
| 90 | line = line + ' for %s' % (users.keys()[0],) | ||
| 91 | else: | ||
| 92 | line = line + ' for %d users' % len(users) | ||
| 93 | if userseen: | ||
| 94 | if aheadjobs == 0: | ||
| 95 | line = line + ' (%s first)' % thisuser | ||
| 96 | else: | ||
| 97 | line = line + ' (%d K before %s)' % ( | ||
| 98 | (aheadbytes+1023)/1024, thisuser) | ||
| 99 | lines.append(line) | ||
| 100 | # | ||
| 101 | sts = pipe.close() | ||
| 102 | if sts: | ||
| 103 | lines.append('lpq exit status %r' % (sts,)) | ||
| 104 | return string.joinfields(lines, ': ') | ||
| 105 | |||
| 106 | if __name__ == "__main__": | ||
| 107 | try: | ||
| 108 | main() | ||
| 109 | except KeyboardInterrupt: | ||
| 110 | pass | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Like mkdir, but also make intermediate directories if necessary. | ||
| 4 | # It is not an error if the given directory already exists (as long | ||
| 5 | # as it is a directory). | ||
| 6 | # Errors are not treated specially -- you just get a Python exception. | ||
| 7 | |||
| 8 | import sys, os | ||
| 9 | |||
| 10 | def main(): | ||
| 11 | for p in sys.argv[1:]: | ||
| 12 | makedirs(p) | ||
| 13 | |||
| 14 | def makedirs(p): | ||
| 15 | if p and not os.path.isdir(p): | ||
| 16 | head, tail = os.path.split(p) | ||
| 17 | makedirs(head) | ||
| 18 | os.mkdir(p, 0777) | ||
| 19 | |||
| 20 | if __name__ == "__main__": | ||
| 21 | main() | ||
| @@ -0,0 +1,117 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | class Markov: | ||
| 4 | def __init__(self, histsize, choice): | ||
| 5 | self.histsize = histsize | ||
| 6 | self.choice = choice | ||
| 7 | self.trans = {} | ||
| 8 | def add(self, state, next): | ||
| 9 | if not self.trans.has_key(state): | ||
| 10 | self.trans[state] = [next] | ||
| 11 | else: | ||
| 12 | self.trans[state].append(next) | ||
| 13 | def put(self, seq): | ||
| 14 | n = self.histsize | ||
| 15 | add = self.add | ||
| 16 | add(None, seq[:0]) | ||
| 17 | for i in range(len(seq)): | ||
| 18 | add(seq[max(0, i-n):i], seq[i:i+1]) | ||
| 19 | add(seq[len(seq)-n:], None) | ||
| 20 | def get(self): | ||
| 21 | choice = self.choice | ||
| 22 | trans = self.trans | ||
| 23 | n = self.histsize | ||
| 24 | seq = choice(trans[None]) | ||
| 25 | while 1: | ||
| 26 | subseq = seq[max(0, len(seq)-n):] | ||
| 27 | options = trans[subseq] | ||
| 28 | next = choice(options) | ||
| 29 | if not next: break | ||
| 30 | seq = seq + next | ||
| 31 | return seq | ||
| 32 | |||
| 33 | def test(): | ||
| 34 | import sys, string, random, getopt | ||
| 35 | args = sys.argv[1:] | ||
| 36 | try: | ||
| 37 | opts, args = getopt.getopt(args, '0123456789cdw') | ||
| 38 | except getopt.error: | ||
| 39 | print 'Usage: markov [-#] [-cddqw] [file] ...' | ||
| 40 | print 'Options:' | ||
| 41 | print '-#: 1-digit history size (default 2)' | ||
| 42 | print '-c: characters (default)' | ||
| 43 | print '-w: words' | ||
| 44 | print '-d: more debugging output' | ||
| 45 | print '-q: no debugging output' | ||
| 46 | print 'Input files (default stdin) are split in paragraphs' | ||
| 47 | print 'separated blank lines and each paragraph is split' | ||
| 48 | print 'in words by whitespace, then reconcatenated with' | ||
| 49 | print 'exactly one space separating words.' | ||
| 50 | print 'Output consists of paragraphs separated by blank' | ||
| 51 | print 'lines, where lines are no longer than 72 characters.' | ||
| 52 | histsize = 2 | ||
| 53 | do_words = 0 | ||
| 54 | debug = 1 | ||
| 55 | for o, a in opts: | ||
| 56 | if '-0' <= o <= '-9': histsize = eval(o[1:]) | ||
| 57 | if o == '-c': do_words = 0 | ||
| 58 | if o == '-d': debug = debug + 1 | ||
| 59 | if o == '-q': debug = 0 | ||
| 60 | if o == '-w': do_words = 1 | ||
| 61 | if not args: args = ['-'] | ||
| 62 | m = Markov(histsize, random.choice) | ||
| 63 | try: | ||
| 64 | for filename in args: | ||
| 65 | if filename == '-': | ||
| 66 | f = sys.stdin | ||
| 67 | if f.isatty(): | ||
| 68 | print 'Sorry, need stdin from file' | ||
| 69 | continue | ||
| 70 | else: | ||
| 71 | f = open(filename, 'r') | ||
| 72 | if debug: print 'processing', filename, '...' | ||
| 73 | text = f.read() | ||
| 74 | f.close() | ||
| 75 | paralist = string.splitfields(text, '\n\n') | ||
| 76 | for para in paralist: | ||
| 77 | if debug > 1: print 'feeding ...' | ||
| 78 | words = string.split(para) | ||
| 79 | if words: | ||
| 80 | if do_words: data = tuple(words) | ||
| 81 | else: data = string.joinfields(words, ' ') | ||
| 82 | m.put(data) | ||
| 83 | except KeyboardInterrupt: | ||
| 84 | print 'Interrupted -- continue with data read so far' | ||
| 85 | if not m.trans: | ||
| 86 | print 'No valid input files' | ||
| 87 | return | ||
| 88 | if debug: print 'done.' | ||
| 89 | if debug > 1: | ||
| 90 | for key in m.trans.keys(): | ||
| 91 | if key is None or len(key) < histsize: | ||
| 92 | print repr(key), m.trans[key] | ||
| 93 | if histsize == 0: print repr(''), m.trans[''] | ||
| 94 | |||
| 95 | while 1: | ||
| 96 | data = m.get() | ||
| 97 | if do_words: words = data | ||
| 98 | else: words = string.split(data) | ||
| 99 | n = 0 | ||
| 100 | limit = 72 | ||
| 101 | for w in words: | ||
| 102 | if n + len(w) > limit: | ||
| 103 | |||
| 104 | n = 0 | ||
| 105 | print w, | ||
| 106 | n = n + len(w) + 1 | ||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | def tuple(list): | ||
| 111 | if len(list) == 0: return () | ||
| 112 | if len(list) == 1: return (list[0],) | ||
| 113 | i = len(list)/2 | ||
| 114 | return tuple(list[:i]) + tuple(list[i:]) | ||
| 115 | |||
| 116 | if __name__ == "__main__": | ||
| 117 | test() | ||
| @@ -0,0 +1,124 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Convert MH directories (1 message per file) or MMDF mailboxes (4x^A | ||
| 4 | # delimited) to unix mailbox (From ... delimited) on stdout. | ||
| 5 | # If -f is given, files contain one message per file (e.g. MH messages) | ||
| 6 | |||
| 7 | import rfc822 | ||
| 8 | import sys | ||
| 9 | import time | ||
| 10 | import os | ||
| 11 | import stat | ||
| 12 | import getopt | ||
| 13 | import re | ||
| 14 | |||
| 15 | def main(): | ||
| 16 | dofile = mmdf | ||
| 17 | try: | ||
| 18 | opts, args = getopt.getopt(sys.argv[1:], 'f') | ||
| 19 | except getopt.error, msg: | ||
| 20 | sys.stderr.write('%s\n' % msg) | ||
| 21 | sys.exit(2) | ||
| 22 | for o, a in opts: | ||
| 23 | if o == '-f': | ||
| 24 | dofile = message | ||
| 25 | if not args: | ||
| 26 | args = ['-'] | ||
| 27 | sts = 0 | ||
| 28 | for arg in args: | ||
| 29 | if arg == '-' or arg == '': | ||
| 30 | sts = dofile(sys.stdin) or sts | ||
| 31 | elif os.path.isdir(arg): | ||
| 32 | sts = mh(arg) or sts | ||
| 33 | elif os.path.isfile(arg): | ||
| 34 | try: | ||
| 35 | f = open(arg) | ||
| 36 | except IOError, msg: | ||
| 37 | sys.stderr.write('%s: %s\n' % (arg, msg)) | ||
| 38 | sts = 1 | ||
| 39 | continue | ||
| 40 | sts = dofile(f) or sts | ||
| 41 | f.close() | ||
| 42 | else: | ||
| 43 | sys.stderr.write('%s: not found\n' % arg) | ||
| 44 | sts = 1 | ||
| 45 | if sts: | ||
| 46 | sys.exit(sts) | ||
| 47 | |||
| 48 | numeric = re.compile('[1-9][0-9]*') | ||
| 49 | |||
| 50 | def mh(dir): | ||
| 51 | sts = 0 | ||
| 52 | msgs = os.listdir(dir) | ||
| 53 | for msg in msgs: | ||
| 54 | if numeric.match(msg) != len(msg): | ||
| 55 | continue | ||
| 56 | fn = os.path.join(dir, msg) | ||
| 57 | try: | ||
| 58 | f = open(fn) | ||
| 59 | except IOError, msg: | ||
| 60 | sys.stderr.write('%s: %s\n' % (fn, msg)) | ||
| 61 | sts = 1 | ||
| 62 | continue | ||
| 63 | sts = message(f) or sts | ||
| 64 | return sts | ||
| 65 | |||
| 66 | def mmdf(f): | ||
| 67 | sts = 0 | ||
| 68 | while 1: | ||
| 69 | line = f.readline() | ||
| 70 | if not line: | ||
| 71 | break | ||
| 72 | if line == '\1\1\1\1\n': | ||
| 73 | sts = message(f, line) or sts | ||
| 74 | else: | ||
| 75 | sys.stderr.write( | ||
| 76 | 'Bad line in MMFD mailbox: %r\n' % (line,)) | ||
| 77 | return sts | ||
| 78 | |||
| 79 | counter = 0 # for generating unique Message-ID headers | ||
| 80 | |||
| 81 | def message(f, delimiter = ''): | ||
| 82 | sts = 0 | ||
| 83 | # Parse RFC822 header | ||
| 84 | m = rfc822.Message(f) | ||
| 85 | # Write unix header line | ||
| 86 | fullname, email = m.getaddr('From') | ||
| 87 | tt = m.getdate('Date') | ||
| 88 | if tt: | ||
| 89 | t = time.mktime(tt) | ||
| 90 | else: | ||
| 91 | sys.stderr.write( | ||
| 92 | 'Unparseable date: %r\n' % (m.getheader('Date'),)) | ||
| 93 | t = os.fstat(f.fileno())[stat.ST_MTIME] | ||
| 94 | print 'From', email, time.ctime(t) | ||
| 95 | # Copy RFC822 header | ||
| 96 | for line in m.headers: | ||
| 97 | print line, | ||
| 98 | # Invent Message-ID header if none is present | ||
| 99 | if not m.has_key('message-id'): | ||
| 100 | global counter | ||
| 101 | counter = counter + 1 | ||
| 102 | msgid = "<%s.%d>" % (hex(t), counter) | ||
| 103 | sys.stderr.write("Adding Message-ID %s (From %s)\n" % | ||
| 104 | (msgid, email)) | ||
| 105 | print "Message-ID:", msgid | ||
| 106 | |||
| 107 | # Copy body | ||
| 108 | while 1: | ||
| 109 | line = f.readline() | ||
| 110 | if line == delimiter: | ||
| 111 | break | ||
| 112 | if not line: | ||
| 113 | sys.stderr.write('Unexpected EOF in message\n') | ||
| 114 | sts = 1 | ||
| 115 | break | ||
| 116 | if line[:5] == 'From ': | ||
| 117 | line = '>' + line | ||
| 118 | print line, | ||
| 119 | # Print trailing newline | ||
| 120 | |||
| 121 | return sts | ||
| 122 | |||
| 123 | if __name__ == "__main__": | ||
| 124 | main() | ||
| @@ -0,0 +1,61 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # A rather specialized script to make sure that a symbolic link named | ||
| 4 | # RCS exists pointing to a real RCS directory in a parallel tree | ||
| 5 | # referenced as RCStree in an ancestor directory. | ||
| 6 | # (I use this because I like my RCS files to reside on a physically | ||
| 7 | # different machine). | ||
| 8 | |||
| 9 | import os | ||
| 10 | |||
| 11 | def main(): | ||
| 12 | rcstree = 'RCStree' | ||
| 13 | rcs = 'RCS' | ||
| 14 | if os.path.islink(rcs): | ||
| 15 | print '%r is a symlink to %r' % (rcs, os.readlink(rcs)) | ||
| 16 | return | ||
| 17 | if os.path.isdir(rcs): | ||
| 18 | print '%r is an ordinary directory' % (rcs,) | ||
| 19 | return | ||
| 20 | if os.path.exists(rcs): | ||
| 21 | print '%r is a file?!?!' % (rcs,) | ||
| 22 | return | ||
| 23 | # | ||
| 24 | p = os.getcwd() | ||
| 25 | up = '' | ||
| 26 | down = '' | ||
| 27 | # Invariants: | ||
| 28 | # (1) join(p, down) is the current directory | ||
| 29 | # (2) up is the same directory as p | ||
| 30 | # Ergo: | ||
| 31 | # (3) join(up, down) is the current directory | ||
| 32 | #print 'p =', repr(p) | ||
| 33 | while not os.path.isdir(os.path.join(p, rcstree)): | ||
| 34 | head, tail = os.path.split(p) | ||
| 35 | #print 'head = %r; tail = %r' % (head, tail) | ||
| 36 | if not tail: | ||
| 37 | print 'Sorry, no ancestor dir contains %r' % (rcstree,) | ||
| 38 | return | ||
| 39 | p = head | ||
| 40 | up = os.path.join(os.pardir, up) | ||
| 41 | down = os.path.join(tail, down) | ||
| 42 | #print 'p = %r; up = %r; down = %r' % (p, up, down) | ||
| 43 | there = os.path.join(up, rcstree) | ||
| 44 | there = os.path.join(there, down) | ||
| 45 | there = os.path.join(there, rcs) | ||
| 46 | if os.path.isdir(there): | ||
| 47 | print '%r already exists' % (there, ) | ||
| 48 | else: | ||
| 49 | print 'making %r' % (there,) | ||
| 50 | makedirs(there) | ||
| 51 | print 'making symlink %r -> %r' % (rcs, there) | ||
| 52 | os.symlink(there, rcs) | ||
| 53 | |||
| 54 | def makedirs(p): | ||
| 55 | if not os.path.isdir(p): | ||
| 56 | head, tail = os.path.split(p) | ||
| 57 | makedirs(head) | ||
| 58 | os.mkdir(p, 0777) | ||
| 59 | |||
| 60 | if __name__ == "__main__": | ||
| 61 | main() | ||
| @@ -0,0 +1,149 @@ | |||
| 1 | # DAH should be three DOTs. | ||
| 2 | # Space between DOTs and DAHs should be one DOT. | ||
| 3 | # Space between two letters should be one DAH. | ||
| 4 | # Space between two words should be DOT DAH DAH. | ||
| 5 | |||
| 6 | import sys, math, audiodev | ||
| 7 | |||
| 8 | DOT = 30 | ||
| 9 | DAH = 3 * DOT | ||
| 10 | OCTAVE = 2 # 1 == 441 Hz, 2 == 882 Hz, ... | ||
| 11 | |||
| 12 | morsetab = { | ||
| 13 | 'A': '.-', 'a': '.-', | ||
| 14 | 'B': '-...', 'b': '-...', | ||
| 15 | 'C': '-.-.', 'c': '-.-.', | ||
| 16 | 'D': '-..', 'd': '-..', | ||
| 17 | 'E': '.', 'e': '.', | ||
| 18 | 'F': '..-.', 'f': '..-.', | ||
| 19 | 'G': '--.', 'g': '--.', | ||
| 20 | 'H': '....', 'h': '....', | ||
| 21 | 'I': '..', 'i': '..', | ||
| 22 | 'J': '.---', 'j': '.---', | ||
| 23 | 'K': '-.-', 'k': '-.-', | ||
| 24 | 'L': '.-..', 'l': '.-..', | ||
| 25 | 'M': '--', 'm': '--', | ||
| 26 | 'N': '-.', 'n': '-.', | ||
| 27 | 'O': '---', 'o': '---', | ||
| 28 | 'P': '.--.', 'p': '.--.', | ||
| 29 | 'Q': '--.-', 'q': '--.-', | ||
| 30 | 'R': '.-.', 'r': '.-.', | ||
| 31 | 'S': '...', 's': '...', | ||
| 32 | 'T': '-', 't': '-', | ||
| 33 | 'U': '..-', 'u': '..-', | ||
| 34 | 'V': '...-', 'v': '...-', | ||
| 35 | 'W': '.--', 'w': '.--', | ||
| 36 | 'X': '-..-', 'x': '-..-', | ||
| 37 | 'Y': '-.--', 'y': '-.--', | ||
| 38 | 'Z': '--..', 'z': '--..', | ||
| 39 | '0': '-----', | ||
| 40 | '1': '.----', | ||
| 41 | '2': '..---', | ||
| 42 | '3': '...--', | ||
| 43 | '4': '....-', | ||
| 44 | '5': '.....', | ||
| 45 | '6': '-....', | ||
| 46 | '7': '--...', | ||
| 47 | '8': '---..', | ||
| 48 | '9': '----.', | ||
| 49 | ',': '--..--', | ||
| 50 | '.': '.-.-.-', | ||
| 51 | '?': '..--..', | ||
| 52 | ';': '-.-.-.', | ||
| 53 | ':': '---...', | ||
| 54 | "'": '.----.', | ||
| 55 | '-': '-....-', | ||
| 56 | '/': '-..-.', | ||
| 57 | '(': '-.--.-', | ||
| 58 | ')': '-.--.-', | ||
| 59 | '_': '..--.-', | ||
| 60 | ' ': ' ' | ||
| 61 | } | ||
| 62 | |||
| 63 | # If we play at 44.1 kHz (which we do), then if we produce one sine | ||
| 64 | # wave in 100 samples, we get a tone of 441 Hz. If we produce two | ||
| 65 | # sine waves in these 100 samples, we get a tone of 882 Hz. 882 Hz | ||
| 66 | # appears to be a nice one for playing morse code. | ||
| 67 | def mkwave(octave): | ||
| 68 | global sinewave, nowave | ||
| 69 | sinewave = '' | ||
| 70 | for i in range(100): | ||
| 71 | val = int(math.sin(math.pi * float(i) * octave / 50.0) * 30000) | ||
| 72 | sinewave = sinewave + chr((val >> 8) & 255) + chr(val & 255) | ||
| 73 | nowave = '\0' * 200 | ||
| 74 | |||
| 75 | mkwave(OCTAVE) | ||
| 76 | |||
| 77 | def main(): | ||
| 78 | import getopt, string | ||
| 79 | try: | ||
| 80 | opts, args = getopt.getopt(sys.argv[1:], 'o:p:') | ||
| 81 | except getopt.error: | ||
| 82 | sys.stderr.write('Usage ' + sys.argv[0] + | ||
| 83 | ' [ -o outfile ] [ args ] ...\n') | ||
| 84 | sys.exit(1) | ||
| 85 | dev = None | ||
| 86 | for o, a in opts: | ||
| 87 | if o == '-o': | ||
| 88 | import aifc | ||
| 89 | dev = aifc.open(a, 'w') | ||
| 90 | dev.setframerate(44100) | ||
| 91 | dev.setsampwidth(2) | ||
| 92 | dev.setnchannels(1) | ||
| 93 | if o == '-p': | ||
| 94 | mkwave(string.atoi(a)) | ||
| 95 | if not dev: | ||
| 96 | import audiodev | ||
| 97 | dev = audiodev.AudioDev() | ||
| 98 | dev.setoutrate(44100) | ||
| 99 | dev.setsampwidth(2) | ||
| 100 | dev.setnchannels(1) | ||
| 101 | dev.close = dev.stop | ||
| 102 | dev.writeframesraw = dev.writeframes | ||
| 103 | if args: | ||
| 104 | line = string.join(args) | ||
| 105 | else: | ||
| 106 | line = sys.stdin.readline() | ||
| 107 | while line: | ||
| 108 | mline = morse(line) | ||
| 109 | play(mline, dev) | ||
| 110 | if hasattr(dev, 'wait'): | ||
| 111 | dev.wait() | ||
| 112 | if not args: | ||
| 113 | line = sys.stdin.readline() | ||
| 114 | else: | ||
| 115 | line = '' | ||
| 116 | dev.close() | ||
| 117 | |||
| 118 | # Convert a string to morse code with \001 between the characters in | ||
| 119 | # the string. | ||
| 120 | def morse(line): | ||
| 121 | res = '' | ||
| 122 | for c in line: | ||
| 123 | try: | ||
| 124 | res = res + morsetab[c] + '\001' | ||
| 125 | except KeyError: | ||
| 126 | pass | ||
| 127 | return res | ||
| 128 | |||
| 129 | # Play a line of morse code. | ||
| 130 | def play(line, dev): | ||
| 131 | for c in line: | ||
| 132 | if c == '.': | ||
| 133 | sine(dev, DOT) | ||
| 134 | elif c == '-': | ||
| 135 | sine(dev, DAH) | ||
| 136 | else: # space | ||
| 137 | pause(dev, DAH + DOT) | ||
| 138 | pause(dev, DOT) | ||
| 139 | |||
| 140 | def sine(dev, length): | ||
| 141 | for i in range(length): | ||
| 142 | dev.writeframesraw(sinewave) | ||
| 143 | |||
| 144 | def pause(dev, length): | ||
| 145 | for i in range(length): | ||
| 146 | dev.writeframesraw(nowave) | ||
| 147 | |||
| 148 | if __name__ == '__main__' or sys.argv[0] == __name__: | ||
| 149 | main() | ||
| @@ -0,0 +1,59 @@ | |||
| 1 | NEWSLIST | ||
| 2 | ======== | ||
| 3 | A program to assist HTTP browsing of newsgroups | ||
| 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 5 | |||
| 6 | WWW browsers such as NCSA Mosaic allow the user to read newsgroup | ||
| 7 | articles by specifying the group name in a URL eg 'news:comp.answers'. | ||
| 8 | |||
| 9 | To browse through many groups, though, (and there are several thousand | ||
| 10 | of them) you really need a page or pages containing links to all the | ||
| 11 | groups. There are some good ones out there, for example, | ||
| 12 | |||
| 13 | http://info.cern.ch/hypertext/DataSources/News/Groups/Overview.html | ||
| 14 | |||
| 15 | is the standard one at CERN, but it only shows the groups available there, | ||
| 16 | which may be rather different from those available on your machine. | ||
| 17 | |||
| 18 | Newslist is a program which creates a hierarchy of pages for you based | ||
| 19 | on the groups available from YOUR server. It is written in python - a | ||
| 20 | splendid interpreted object-oriented language which I suggest you get | ||
| 21 | right now from the directory /pub/python at ftp.cwi.nl, if you haven't | ||
| 22 | already got it. | ||
| 23 | |||
| 24 | You should be able to see some sample output by looking at: | ||
| 25 | http://pelican.cl.cam.ac.uk/newspage/root.html | ||
| 26 | |||
| 27 | Descriptions of newsgroups can be added from a file with one group | ||
| 28 | per line. eg: | ||
| 29 | |||
| 30 | alt.foo Articles about foo | ||
| 31 | comp.bar Programming in 'bar' and related languages | ||
| 32 | |||
| 33 | A suitable list detailing most groups can be found at ftp.uu.net in | ||
| 34 | /uunet-info/newsgroups.gz. | ||
| 35 | |||
| 36 | Make sure you read the information at the beginning of the program source and | ||
| 37 | configure the variables before running. | ||
| 38 | |||
| 39 | In addition to python, you need: | ||
| 40 | |||
| 41 | An NNTP-based news feed. | ||
| 42 | A directory in which to put the pages. | ||
| 43 | |||
| 44 | The programming is not very beautiful, but it works! It comes with no | ||
| 45 | warranty, express or implied, but with the hope that some others may | ||
| 46 | find it useful. | ||
| 47 | |||
| 48 | Comments, improvements & suggestions welcomed. | ||
| 49 | Quentin Stafford-Fraser | ||
| 50 | |||
| 51 | ---------------------------------------------------------------------- | ||
| 52 | Quentin Stafford-Fraser | ||
| 53 | http://pelican.cl.cam.ac.uk/people/qs101/me.html | ||
| 54 | |||
| 55 | Cambridge University Computer Lab Rank Xerox Cambridge EuroPARC | ||
| 56 | qs101@cl.cam.ac.uk fraser@europarc.xerox.com | ||
| 57 | Tel: +44 223 334411 Tel: +44 223 341521 | ||
| 58 | Fax: +44 223 334679 Fax: +44 223 341510 | ||
| 59 | ---------------------------------------------------------------------- | ||
| @@ -0,0 +1,366 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | ####################################################################### | ||
| 3 | # Newslist $Revision: 37320 $ | ||
| 4 | # | ||
| 5 | # Syntax: | ||
| 6 | # newslist [ -a ] | ||
| 7 | # | ||
| 8 | # This is a program to create a directory full of HTML pages | ||
| 9 | # which between them contain links to all the newsgroups available | ||
| 10 | # on your server. | ||
| 11 | # | ||
| 12 | # The -a option causes a complete list of all groups to be read from | ||
| 13 | # the server rather than just the ones which have appeared since last | ||
| 14 | # execution. This recreates the local list from scratch. Use this on | ||
| 15 | # the first invocation of the program, and from time to time thereafter. | ||
| 16 | # When new groups are first created they may appear on your server as | ||
| 17 | # empty groups. By default, empty groups are ignored by the -a option. | ||
| 18 | # However, these new groups will not be created again, and so will not | ||
| 19 | # appear in the server's list of 'new groups' at a later date. Hence it | ||
| 20 | # won't appear until you do a '-a' after some articles have appeared. | ||
| 21 | # | ||
| 22 | # I should really keep a list of ignored empty groups and re-check them | ||
| 23 | # for articles on every run, but I haven't got around to it yet. | ||
| 24 | # | ||
| 25 | # This assumes an NNTP news feed. | ||
| 26 | # | ||
| 27 | # Feel free to copy, distribute and modify this code for | ||
| 28 | # non-commercial use. If you make any useful modifications, let me | ||
| 29 | # know! | ||
| 30 | # | ||
| 31 | # (c) Quentin Stafford-Fraser 1994 | ||
| 32 | # fraser@europarc.xerox.com qs101@cl.cam.ac.uk | ||
| 33 | # # | ||
| 34 | ####################################################################### | ||
| 35 | import sys,nntplib, string, marshal, time, os, posix, string | ||
| 36 | |||
| 37 | ####################################################################### | ||
| 38 | # Check these variables before running! # | ||
| 39 | |||
| 40 | # Top directory. | ||
| 41 | # Filenames which don't start with / are taken as being relative to this. | ||
| 42 | topdir='/anfs/qsbigdisc/web/html/newspage' | ||
| 43 | |||
| 44 | # The name of your NNTP host | ||
| 45 | # eg. | ||
| 46 | # newshost = 'nntp-serv.cl.cam.ac.uk' | ||
| 47 | # or use following to get the name from the NNTPSERVER environment | ||
| 48 | # variable: | ||
| 49 | # newshost = posix.environ['NNTPSERVER'] | ||
| 50 | newshost = 'nntp-serv.cl.cam.ac.uk' | ||
| 51 | |||
| 52 | # The filename for a local cache of the newsgroup list | ||
| 53 | treefile = 'grouptree' | ||
| 54 | |||
| 55 | # The filename for descriptions of newsgroups | ||
| 56 | # I found a suitable one at ftp.uu.net in /uunet-info/newgroups.gz | ||
| 57 | # You can set this to '' if you don't wish to use one. | ||
| 58 | descfile = 'newsgroups' | ||
| 59 | |||
| 60 | # The directory in which HTML pages should be created | ||
| 61 | # eg. | ||
| 62 | # pagedir = '/usr/local/lib/html/newspage' | ||
| 63 | # pagedir = 'pages' | ||
| 64 | pagedir = topdir | ||
| 65 | |||
| 66 | # The html prefix which will refer to this directory | ||
| 67 | # eg. | ||
| 68 | # httppref = '/newspage/', | ||
| 69 | # or leave blank for relative links between pages: (Recommended) | ||
| 70 | # httppref = '' | ||
| 71 | httppref = '' | ||
| 72 | |||
| 73 | # The name of the 'root' news page in this directory. | ||
| 74 | # A .html suffix will be added. | ||
| 75 | rootpage = 'root' | ||
| 76 | |||
| 77 | # Set skipempty to 0 if you wish to see links to empty groups as well. | ||
| 78 | # Only affects the -a option. | ||
| 79 | skipempty = 1 | ||
| 80 | |||
| 81 | # pagelinkicon can contain html to put an icon after links to | ||
| 82 | # further pages. This helps to make important links stand out. | ||
| 83 | # Set to '' if not wanted, or '...' is quite a good one. | ||
| 84 | pagelinkicon='... <img src="http://pelican.cl.cam.ac.uk/icons/page.xbm"> ' | ||
| 85 | |||
| 86 | # --------------------------------------------------------------------- | ||
| 87 | # Less important personal preferences: | ||
| 88 | |||
| 89 | # Sublistsize controls the maximum number of items the will appear as | ||
| 90 | # an indented sub-list before the whole thing is moved onto a different | ||
| 91 | # page. The smaller this is, the more pages you will have, but the | ||
| 92 | # shorter each will be. | ||
| 93 | sublistsize = 4 | ||
| 94 | |||
| 95 | # That should be all. # | ||
| 96 | ####################################################################### | ||
| 97 | |||
| 98 | for dir in os.curdir, os.environ['HOME']: | ||
| 99 | rcfile = os.path.join(dir, '.newslistrc.py') | ||
| 100 | if os.path.exists(rcfile): | ||
| 101 | print rcfile | ||
| 102 | execfile(rcfile) | ||
| 103 | break | ||
| 104 | |||
| 105 | from nntplib import NNTP | ||
| 106 | from stat import * | ||
| 107 | |||
| 108 | rcsrev = '$Revision: 37320 $' | ||
| 109 | rcsrev = string.join(filter(lambda s: '$' not in s, string.split(rcsrev))) | ||
| 110 | desc = {} | ||
| 111 | |||
| 112 | # Make (possibly) relative filenames into absolute ones | ||
| 113 | treefile = os.path.join(topdir,treefile) | ||
| 114 | descfile = os.path.join(topdir,descfile) | ||
| 115 | page = os.path.join(topdir,pagedir) | ||
| 116 | |||
| 117 | # First the bits for creating trees --------------------------- | ||
| 118 | |||
| 119 | # Addtotree creates/augments a tree from a list of group names | ||
| 120 | def addtotree(tree, groups): | ||
| 121 | print 'Updating tree...' | ||
| 122 | for i in groups: | ||
| 123 | parts = string.splitfields(i,'.') | ||
| 124 | makeleaf(tree, parts) | ||
| 125 | |||
| 126 | # Makeleaf makes a leaf and the branch leading to it if necessary | ||
| 127 | def makeleaf(tree,path): | ||
| 128 | j = path[0] | ||
| 129 | l = len(path) | ||
| 130 | |||
| 131 | if not tree.has_key(j): | ||
| 132 | tree[j] = {} | ||
| 133 | if l == 1: | ||
| 134 | tree[j]['.'] = '.' | ||
| 135 | if l > 1: | ||
| 136 | makeleaf(tree[j],path[1:]) | ||
| 137 | |||
| 138 | # Then the bits for outputting trees as pages ---------------- | ||
| 139 | |||
| 140 | # Createpage creates an HTML file named <root>.html containing links | ||
| 141 | # to those groups beginning with <root>. | ||
| 142 | |||
| 143 | def createpage(root, tree, p): | ||
| 144 | filename = os.path.join(pagedir,root+'.html') | ||
| 145 | if root == rootpage: | ||
| 146 | detail = '' | ||
| 147 | else: | ||
| 148 | detail = ' under ' + root | ||
| 149 | f = open(filename,'w') | ||
| 150 | # f.write('Content-Type: text/html\n') | ||
| 151 | f.write('<TITLE>Newsgroups available' + detail + '</TITLE>\n') | ||
| 152 | f.write('<H1>Newsgroups available' + detail +'</H1>\n') | ||
| 153 | f.write('<A HREF="'+httppref+rootpage+'.html">Back to top level</A><P>\n') | ||
| 154 | printtree(f,tree,0,p) | ||
| 155 | f.write('<I>This page automatically created by \'newslist\' v. '+rcsrev+'.') | ||
| 156 | f.write(time.ctime(time.time()) + '</I><P>') | ||
| 157 | f.close() | ||
| 158 | |||
| 159 | # Printtree prints the groups as a bulleted list. Groups with | ||
| 160 | # more than <sublistsize> subgroups will be put on a separate page. | ||
| 161 | # Other sets of subgroups are just indented. | ||
| 162 | |||
| 163 | def printtree(f, tree, indent, p): | ||
| 164 | global desc | ||
| 165 | l = len(tree) | ||
| 166 | |||
| 167 | if l > sublistsize and indent>0: | ||
| 168 | # Create a new page and a link to it | ||
| 169 | f.write('<LI><B><A HREF="'+httppref+p[1:]+'.html">') | ||
| 170 | f.write(p[1:]+'.*') | ||
| 171 | f.write('</A></B>'+pagelinkicon+'\n') | ||
| 172 | createpage(p[1:], tree, p) | ||
| 173 | return | ||
| 174 | |||
| 175 | kl = tree.keys() | ||
| 176 | |||
| 177 | if l > 1: | ||
| 178 | kl.sort() | ||
| 179 | if indent > 0: | ||
| 180 | # Create a sub-list | ||
| 181 | f.write('<LI>'+p[1:]+'\n<UL>') | ||
| 182 | else: | ||
| 183 | # Create a main list | ||
| 184 | f.write('<UL>') | ||
| 185 | indent = indent + 1 | ||
| 186 | |||
| 187 | for i in kl: | ||
| 188 | if i == '.': | ||
| 189 | # Output a newsgroup | ||
| 190 | f.write('<LI><A HREF="news:' + p[1:] + '">'+ p[1:] + '</A> ') | ||
| 191 | if desc.has_key(p[1:]): | ||
| 192 | f.write(' <I>'+desc[p[1:]]+'</I>\n') | ||
| 193 | else: | ||
| 194 | f.write('\n') | ||
| 195 | else: | ||
| 196 | # Output a hierarchy | ||
| 197 | printtree(f,tree[i], indent, p+'.'+i) | ||
| 198 | |||
| 199 | if l > 1: | ||
| 200 | f.write('\n</UL>') | ||
| 201 | |||
| 202 | # Reading descriptions file --------------------------------------- | ||
| 203 | |||
| 204 | # This returns an array mapping group name to its description | ||
| 205 | |||
| 206 | def readdesc(descfile): | ||
| 207 | global desc | ||
| 208 | |||
| 209 | desc = {} | ||
| 210 | |||
| 211 | if descfile == '': | ||
| 212 | return | ||
| 213 | |||
| 214 | try: | ||
| 215 | d = open(descfile, 'r') | ||
| 216 | print 'Reading descriptions...' | ||
| 217 | except (IOError): | ||
| 218 | print 'Failed to open description file ' + descfile | ||
| 219 | return | ||
| 220 | l = d.readline() | ||
| 221 | while l != '': | ||
| 222 | bits = string.split(l) | ||
| 223 | try: | ||
| 224 | grp = bits[0] | ||
| 225 | dsc = string.join(bits[1:]) | ||
| 226 | if len(dsc)>1: | ||
| 227 | desc[grp] = dsc | ||
| 228 | except (IndexError): | ||
| 229 | pass | ||
| 230 | l = d.readline() | ||
| 231 | |||
| 232 | # Check that ouput directory exists, ------------------------------ | ||
| 233 | # and offer to create it if not | ||
| 234 | |||
| 235 | def checkopdir(pagedir): | ||
| 236 | if not os.path.isdir(pagedir): | ||
| 237 | print 'Directory '+pagedir+' does not exist.' | ||
| 238 | print 'Shall I create it for you? (y/n)' | ||
| 239 | if sys.stdin.readline()[0] == 'y': | ||
| 240 | try: | ||
| 241 | os.mkdir(pagedir,0777) | ||
| 242 | except: | ||
| 243 | print 'Sorry - failed!' | ||
| 244 | sys.exit(1) | ||
| 245 | else: | ||
| 246 | print 'OK. Exiting.' | ||
| 247 | sys.exit(1) | ||
| 248 | |||
| 249 | # Read and write current local tree ---------------------------------- | ||
| 250 | |||
| 251 | def readlocallist(treefile): | ||
| 252 | print 'Reading current local group list...' | ||
| 253 | tree = {} | ||
| 254 | try: | ||
| 255 | treetime = time.localtime(os.stat(treefile)[ST_MTIME]) | ||
| 256 | except: | ||
| 257 | print '\n*** Failed to open local group cache '+treefile | ||
| 258 | print 'If this is the first time you have run newslist, then' | ||
| 259 | print 'use the -a option to create it.' | ||
| 260 | sys.exit(1) | ||
| 261 | treedate = '%02d%02d%02d' % (treetime[0] % 100 ,treetime[1], treetime[2]) | ||
| 262 | try: | ||
| 263 | dump = open(treefile,'r') | ||
| 264 | tree = marshal.load(dump) | ||
| 265 | dump.close() | ||
| 266 | except (IOError): | ||
| 267 | print 'Cannot open local group list ' + treefile | ||
| 268 | return (tree, treedate) | ||
| 269 | |||
| 270 | def writelocallist(treefile, tree): | ||
| 271 | try: | ||
| 272 | dump = open(treefile,'w') | ||
| 273 | groups = marshal.dump(tree,dump) | ||
| 274 | dump.close() | ||
| 275 | print 'Saved list to '+treefile+'\n' | ||
| 276 | except: | ||
| 277 | print 'Sorry - failed to write to local group cache '+treefile | ||
| 278 | print 'Does it (or its directory) have the correct permissions?' | ||
| 279 | sys.exit(1) | ||
| 280 | |||
| 281 | # Return list of all groups on server ----------------------------- | ||
| 282 | |||
| 283 | def getallgroups(server): | ||
| 284 | print 'Getting list of all groups...' | ||
| 285 | treedate='010101' | ||
| 286 | info = server.list()[1] | ||
| 287 | groups = [] | ||
| 288 | print 'Processing...' | ||
| 289 | if skipempty: | ||
| 290 | print '\nIgnoring following empty groups:' | ||
| 291 | for i in info: | ||
| 292 | grpname = string.split(i[0])[0] | ||
| 293 | if skipempty and string.atoi(i[1]) < string.atoi(i[2]): | ||
| 294 | print grpname+' ', | ||
| 295 | else: | ||
| 296 | groups.append(grpname) | ||
| 297 | print '\n' | ||
| 298 | if skipempty: | ||
| 299 | print '(End of empty groups)' | ||
| 300 | return groups | ||
| 301 | |||
| 302 | # Return list of new groups on server ----------------------------- | ||
| 303 | |||
| 304 | def getnewgroups(server, treedate): | ||
| 305 | print 'Getting list of new groups since start of '+treedate+'...', | ||
| 306 | info = server.newgroups(treedate,'000001')[1] | ||
| 307 | print 'got %d.' % len(info) | ||
| 308 | print 'Processing...', | ||
| 309 | groups = [] | ||
| 310 | for i in info: | ||
| 311 | grpname = string.split(i)[0] | ||
| 312 | groups.append(grpname) | ||
| 313 | print 'Done' | ||
| 314 | return groups | ||
| 315 | |||
| 316 | # Now the main program -------------------------------------------- | ||
| 317 | |||
| 318 | def main(): | ||
| 319 | global desc | ||
| 320 | |||
| 321 | tree={} | ||
| 322 | |||
| 323 | # Check that the output directory exists | ||
| 324 | checkopdir(pagedir); | ||
| 325 | |||
| 326 | try: | ||
| 327 | print 'Connecting to '+newshost+'...' | ||
| 328 | if sys.version[0] == '0': | ||
| 329 | s = NNTP.init(newshost) | ||
| 330 | else: | ||
| 331 | s = NNTP(newshost) | ||
| 332 | connected = 1 | ||
| 333 | except (nntplib.error_temp, nntplib.error_perm), x: | ||
| 334 | print 'Error connecting to host:', x | ||
| 335 | print 'I\'ll try to use just the local list.' | ||
| 336 | connected = 0 | ||
| 337 | |||
| 338 | # If -a is specified, read the full list of groups from server | ||
| 339 | if connected and len(sys.argv) > 1 and sys.argv[1] == '-a': | ||
| 340 | |||
| 341 | groups = getallgroups(s) | ||
| 342 | |||
| 343 | # Otherwise just read the local file and then add | ||
| 344 | # groups created since local file last modified. | ||
| 345 | else: | ||
| 346 | |||
| 347 | (tree, treedate) = readlocallist(treefile) | ||
| 348 | if connected: | ||
| 349 | groups = getnewgroups(s, treedate) | ||
| 350 | |||
| 351 | if connected: | ||
| 352 | addtotree(tree, groups) | ||
| 353 | writelocallist(treefile,tree) | ||
| 354 | |||
| 355 | # Read group descriptions | ||
| 356 | readdesc(descfile) | ||
| 357 | |||
| 358 | print 'Creating pages...' | ||
| 359 | createpage(rootpage, tree, '') | ||
| 360 | print 'Done' | ||
| 361 | |||
| 362 | if __name__ == "__main__": | ||
| 363 | main() | ||
| 364 | |||
| 365 | # That's all folks | ||
| 366 | ###################################################################### | ||
| @@ -0,0 +1,34 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Print digits of pi forever. | ||
| 4 | # | ||
| 5 | # The algorithm, using Python's 'long' integers ("bignums"), works | ||
| 6 | # with continued fractions, and was conceived by Lambert Meertens. | ||
| 7 | # | ||
| 8 | # See also the ABC Programmer's Handbook, by Geurts, Meertens & Pemberton, | ||
| 9 | # published by Prentice-Hall (UK) Ltd., 1990. | ||
| 10 | |||
| 11 | import sys | ||
| 12 | |||
| 13 | def main(): | ||
| 14 | k, a, b, a1, b1 = 2L, 4L, 1L, 12L, 4L | ||
| 15 | while 1: | ||
| 16 | # Next approximation | ||
| 17 | p, q, k = k*k, 2L*k+1L, k+1L | ||
| 18 | a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1 | ||
| 19 | # Print common digits | ||
| 20 | d, d1 = a/b, a1/b1 | ||
| 21 | while d == d1: | ||
| 22 | output(d) | ||
| 23 | a, a1 = 10L*(a%b), 10L*(a1%b1) | ||
| 24 | d, d1 = a/b, a1/b1 | ||
| 25 | |||
| 26 | def output(d): | ||
| 27 | # Use write() to avoid spaces between the digits | ||
| 28 | # Use str() to avoid the 'L' | ||
| 29 | sys.stdout.write(str(d)) | ||
| 30 | # Flush so the output is seen immediately | ||
| 31 | sys.stdout.flush() | ||
| 32 | |||
| 33 | if __name__ == "__main__": | ||
| 34 | main() | ||
| @@ -0,0 +1,130 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Emulate some Perl command line options. | ||
| 4 | # Usage: pp [-a] [-c] [-d] [-e scriptline] [-F fieldsep] [-n] [-p] [file] ... | ||
| 5 | # Where the options mean the following: | ||
| 6 | # -a : together with -n or -p, splits each line into list F | ||
| 7 | # -c : check syntax only, do not execute any code | ||
| 8 | # -d : run the script under the debugger, pdb | ||
| 9 | # -e scriptline : gives one line of the Python script; may be repeated | ||
| 10 | # -F fieldsep : sets the field separator for the -a option [not in Perl] | ||
| 11 | # -n : runs the script for each line of input | ||
| 12 | # -p : prints the line after the script has run | ||
| 13 | # When no script lines have been passed, the first file argument | ||
| 14 | # contains the script. With -n or -p, the remaining arguments are | ||
| 15 | # read as input to the script, line by line. If a file is '-' | ||
| 16 | # or missing, standard input is read. | ||
| 17 | |||
| 18 | # XXX To do: | ||
| 19 | # - add -i extension option (change files in place) | ||
| 20 | # - make a single loop over the files and lines (changes effect of 'break')? | ||
| 21 | # - add an option to specify the record separator | ||
| 22 | # - except for -n/-p, run directly from the file if at all possible | ||
| 23 | |||
| 24 | import sys | ||
| 25 | import string | ||
| 26 | import getopt | ||
| 27 | |||
| 28 | FS = '' | ||
| 29 | SCRIPT = [] | ||
| 30 | AFLAG = 0 | ||
| 31 | CFLAG = 0 | ||
| 32 | DFLAG = 0 | ||
| 33 | NFLAG = 0 | ||
| 34 | PFLAG = 0 | ||
| 35 | |||
| 36 | try: | ||
| 37 | optlist, ARGS = getopt.getopt(sys.argv[1:], 'acde:F:np') | ||
| 38 | except getopt.error, msg: | ||
| 39 | sys.stderr.write(sys.argv[0] + ': ' + msg + '\n') | ||
| 40 | sys.exit(2) | ||
| 41 | |||
| 42 | for option, optarg in optlist: | ||
| 43 | if option == '-a': | ||
| 44 | AFLAG = 1 | ||
| 45 | elif option == '-c': | ||
| 46 | CFLAG = 1 | ||
| 47 | elif option == '-d': | ||
| 48 | DFLAG = 1 | ||
| 49 | elif option == '-e': | ||
| 50 | for line in string.splitfields(optarg, '\n'): | ||
| 51 | SCRIPT.append(line) | ||
| 52 | elif option == '-F': | ||
| 53 | FS = optarg | ||
| 54 | elif option == '-n': | ||
| 55 | NFLAG = 1 | ||
| 56 | PFLAG = 0 | ||
| 57 | elif option == '-p': | ||
| 58 | NFLAG = 1 | ||
| 59 | PFLAG = 1 | ||
| 60 | else: | ||
| 61 | print option, 'not recognized???' | ||
| 62 | |||
| 63 | if not ARGS: ARGS.append('-') | ||
| 64 | |||
| 65 | if not SCRIPT: | ||
| 66 | if ARGS[0] == '-': | ||
| 67 | fp = sys.stdin | ||
| 68 | else: | ||
| 69 | fp = open(ARGS[0], 'r') | ||
| 70 | while 1: | ||
| 71 | line = fp.readline() | ||
| 72 | if not line: break | ||
| 73 | SCRIPT.append(line[:-1]) | ||
| 74 | del fp | ||
| 75 | del ARGS[0] | ||
| 76 | if not ARGS: ARGS.append('-') | ||
| 77 | |||
| 78 | if CFLAG: | ||
| 79 | prologue = ['if 0:'] | ||
| 80 | epilogue = [] | ||
| 81 | elif NFLAG: | ||
| 82 | # Note that it is on purpose that AFLAG and PFLAG are | ||
| 83 | # tested dynamically each time through the loop | ||
| 84 | prologue = [ \ | ||
| 85 | 'LINECOUNT = 0', \ | ||
| 86 | 'for FILE in ARGS:', \ | ||
| 87 | ' \tif FILE == \'-\':', \ | ||
| 88 | ' \t \tFP = sys.stdin', \ | ||
| 89 | ' \telse:', \ | ||
| 90 | ' \t \tFP = open(FILE, \'r\')', \ | ||
| 91 | ' \tLINENO = 0', \ | ||
| 92 | ' \twhile 1:', \ | ||
| 93 | ' \t \tLINE = FP.readline()', \ | ||
| 94 | ' \t \tif not LINE: break', \ | ||
| 95 | ' \t \tLINENO = LINENO + 1', \ | ||
| 96 | ' \t \tLINECOUNT = LINECOUNT + 1', \ | ||
| 97 | ' \t \tL = LINE[:-1]', \ | ||
| 98 | ' \t \taflag = AFLAG', \ | ||
| 99 | ' \t \tif aflag:', \ | ||
| 100 | ' \t \t \tif FS: F = string.splitfields(L, FS)', \ | ||
| 101 | ' \t \t \telse: F = string.split(L)' \ | ||
| 102 | ] | ||
| 103 | epilogue = [ \ | ||
| 104 | ' \t \tif not PFLAG: continue', \ | ||
| 105 | ' \t \tif aflag:', \ | ||
| 106 | ' \t \t \tif FS: print string.joinfields(F, FS)', \ | ||
| 107 | ' \t \t \telse: print string.join(F)', \ | ||
| 108 | ' \t \telse: print L', \ | ||
| 109 | ] | ||
| 110 | else: | ||
| 111 | prologue = ['if 1:'] | ||
| 112 | epilogue = [] | ||
| 113 | |||
| 114 | # Note that we indent using tabs only, so that any indentation style | ||
| 115 | # used in 'command' will come out right after re-indentation. | ||
| 116 | |||
| 117 | program = string.joinfields(prologue, '\n') + '\n' | ||
| 118 | for line in SCRIPT: | ||
| 119 | program = program + (' \t \t' + line + '\n') | ||
| 120 | program = program + (string.joinfields(epilogue, '\n') + '\n') | ||
| 121 | |||
| 122 | import tempfile | ||
| 123 | fp = tempfile.NamedTemporaryFile() | ||
| 124 | fp.write(program) | ||
| 125 | fp.flush() | ||
| 126 | if DFLAG: | ||
| 127 | import pdb | ||
| 128 | pdb.run('execfile(%r)' % (tfn,)) | ||
| 129 | else: | ||
| 130 | execfile(tfn) | ||
| @@ -0,0 +1,27 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Print prime numbers in a given range | ||
| 4 | |||
| 5 | def main(): | ||
| 6 | import sys | ||
| 7 | min, max = 2, 0x7fffffff | ||
| 8 | if sys.argv[1:]: | ||
| 9 | min = int(eval(sys.argv[1])) | ||
| 10 | if sys.argv[2:]: | ||
| 11 | max = int(eval(sys.argv[2])) | ||
| 12 | primes(min, max) | ||
| 13 | |||
| 14 | def primes(min, max): | ||
| 15 | if 2 >= min: print 2 | ||
| 16 | primes = [2] | ||
| 17 | i = 3 | ||
| 18 | while i <= max: | ||
| 19 | for p in primes: | ||
| 20 | if i%p == 0 or p*p > i: break | ||
| 21 | if i%p <> 0: | ||
| 22 | primes.append(i) | ||
| 23 | if i >= min: print i | ||
| 24 | i = i+2 | ||
| 25 | |||
| 26 | if __name__ == "__main__": | ||
| 27 | main() | ||
| @@ -0,0 +1,85 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """N queens problem. | ||
| 4 | |||
| 5 | The (well-known) problem is due to Niklaus Wirth. | ||
| 6 | |||
| 7 | This solution is inspired by Dijkstra (Structured Programming). It is | ||
| 8 | a classic recursive backtracking approach. | ||
| 9 | |||
| 10 | """ | ||
| 11 | |||
| 12 | N = 8 # Default; command line overrides | ||
| 13 | |||
| 14 | class Queens: | ||
| 15 | |||
| 16 | def __init__(self, n=N): | ||
| 17 | self.n = n | ||
| 18 | self.reset() | ||
| 19 | |||
| 20 | def reset(self): | ||
| 21 | n = self.n | ||
| 22 | self.y = [None]*n # Where is the queen in column x | ||
| 23 | self.row = [0]*n # Is row[y] safe? | ||
| 24 | self.up = [0] * (2*n-1) # Is upward diagonal[x-y] safe? | ||
| 25 | self.down = [0] * (2*n-1) # Is downward diagonal[x+y] safe? | ||
| 26 | self.nfound = 0 # Instrumentation | ||
| 27 | |||
| 28 | def solve(self, x=0): # Recursive solver | ||
| 29 | for y in range(self.n): | ||
| 30 | if self.safe(x, y): | ||
| 31 | self.place(x, y) | ||
| 32 | if x+1 == self.n: | ||
| 33 | self.display() | ||
| 34 | else: | ||
| 35 | self.solve(x+1) | ||
| 36 | self.remove(x, y) | ||
| 37 | |||
| 38 | def safe(self, x, y): | ||
| 39 | return not self.row[y] and not self.up[x-y] and not self.down[x+y] | ||
| 40 | |||
| 41 | def place(self, x, y): | ||
| 42 | self.y[x] = y | ||
| 43 | self.row[y] = 1 | ||
| 44 | self.up[x-y] = 1 | ||
| 45 | self.down[x+y] = 1 | ||
| 46 | |||
| 47 | def remove(self, x, y): | ||
| 48 | self.y[x] = None | ||
| 49 | self.row[y] = 0 | ||
| 50 | self.up[x-y] = 0 | ||
| 51 | self.down[x+y] = 0 | ||
| 52 | |||
| 53 | silent = 0 # If set, count solutions only | ||
| 54 | |||
| 55 | def display(self): | ||
| 56 | self.nfound = self.nfound + 1 | ||
| 57 | if self.silent: | ||
| 58 | return | ||
| 59 | print '+-' + '--'*self.n + '+' | ||
| 60 | for y in range(self.n-1, -1, -1): | ||
| 61 | print '|', | ||
| 62 | for x in range(self.n): | ||
| 63 | if self.y[x] == y: | ||
| 64 | print "Q", | ||
| 65 | else: | ||
| 66 | print ".", | ||
| 67 | print '|' | ||
| 68 | print '+-' + '--'*self.n + '+' | ||
| 69 | |||
| 70 | def main(): | ||
| 71 | import sys | ||
| 72 | silent = 0 | ||
| 73 | n = N | ||
| 74 | if sys.argv[1:2] == ['-n']: | ||
| 75 | silent = 1 | ||
| 76 | del sys.argv[1] | ||
| 77 | if sys.argv[1:]: | ||
| 78 | n = int(sys.argv[1]) | ||
| 79 | q = Queens(n) | ||
| 80 | q.silent = silent | ||
| 81 | q.solve() | ||
| 82 | print "Found", q.nfound, "solutions." | ||
| 83 | |||
| 84 | if __name__ == "__main__": | ||
| 85 | main() | ||
| @@ -0,0 +1,33 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | # script.py -- Make typescript of terminal session. | ||
| 3 | # Usage: | ||
| 4 | # -a Append to typescript. | ||
| 5 | # -p Use Python as shell. | ||
| 6 | # Author: Steen Lumholt. | ||
| 7 | |||
| 8 | |||
| 9 | import os, time, sys | ||
| 10 | import pty | ||
| 11 | |||
| 12 | def read(fd): | ||
| 13 | data = os.read(fd, 1024) | ||
| 14 | file.write(data) | ||
| 15 | return data | ||
| 16 | |||
| 17 | shell = 'sh' | ||
| 18 | filename = 'typescript' | ||
| 19 | mode = 'w' | ||
| 20 | if os.environ.has_key('SHELL'): | ||
| 21 | shell = os.environ['SHELL'] | ||
| 22 | if '-a' in sys.argv: | ||
| 23 | mode = 'a' | ||
| 24 | if '-p' in sys.argv: | ||
| 25 | shell = 'python' | ||
| 26 | |||
| 27 | file = open(filename, mode) | ||
| 28 | |||
| 29 | sys.stdout.write('Script started, file is %s\n' % filename) | ||
| 30 | file.write('Script started on %s\n' % time.ctime(time.time())) | ||
| 31 | pty.spawn(shell, read) | ||
| 32 | file.write('Script done on %s\n' % time.ctime(time.time())) | ||
| 33 | sys.stdout.write('Script done, file is %s\n' % filename) | ||
| @@ -0,0 +1,107 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Calculate your unbirthday count (see Alice in Wonderland). | ||
| 4 | # This is defined as the number of days from your birth until today | ||
| 5 | # that weren't your birthday. (The day you were born is not counted). | ||
| 6 | # Leap years make it interesting. | ||
| 7 | |||
| 8 | import sys | ||
| 9 | import time | ||
| 10 | import calendar | ||
| 11 | |||
| 12 | def main(): | ||
| 13 | # Note that the range checks below also check for bad types, | ||
| 14 | # e.g. 3.14 or (). However syntactically invalid replies | ||
| 15 | # will raise an exception. | ||
| 16 | if sys.argv[1:]: | ||
| 17 | year = int(sys.argv[1]) | ||
| 18 | else: | ||
| 19 | year = int(raw_input('In which year were you born? ')) | ||
| 20 | if 0<=year<100: | ||
| 21 | print "I'll assume that by", year, | ||
| 22 | year = year + 1900 | ||
| 23 | print 'you mean', year, 'and not the early Christian era' | ||
| 24 | elif not (1850<=year<=2002): | ||
| 25 | print "It's hard to believe you were born in", year | ||
| 26 | return | ||
| 27 | # | ||
| 28 | if sys.argv[2:]: | ||
| 29 | month = int(sys.argv[2]) | ||
| 30 | else: | ||
| 31 | month = int(raw_input('And in which month? (1-12) ')) | ||
| 32 | if not (1<=month<=12): | ||
| 33 | print 'There is no month numbered', month | ||
| 34 | return | ||
| 35 | # | ||
| 36 | if sys.argv[3:]: | ||
| 37 | day = int(sys.argv[3]) | ||
| 38 | else: | ||
| 39 | day = int(raw_input('And on what day of that month? (1-31) ')) | ||
| 40 | if month == 2 and calendar.isleap(year): | ||
| 41 | maxday = 29 | ||
| 42 | else: | ||
| 43 | maxday = calendar.mdays[month] | ||
| 44 | if not (1<=day<=maxday): | ||
| 45 | print 'There are no', day, 'days in that month!' | ||
| 46 | return | ||
| 47 | # | ||
| 48 | bdaytuple = (year, month, day) | ||
| 49 | bdaydate = mkdate(bdaytuple) | ||
| 50 | print 'You were born on', format(bdaytuple) | ||
| 51 | # | ||
| 52 | todaytuple = time.localtime()[:3] | ||
| 53 | todaydate = mkdate(todaytuple) | ||
| 54 | print 'Today is', format(todaytuple) | ||
| 55 | # | ||
| 56 | if bdaytuple > todaytuple: | ||
| 57 | print 'You are a time traveler. Go back to the future!' | ||
| 58 | return | ||
| 59 | # | ||
| 60 | if bdaytuple == todaytuple: | ||
| 61 | print 'You were born today. Have a nice life!' | ||
| 62 | return | ||
| 63 | # | ||
| 64 | days = todaydate - bdaydate | ||
| 65 | print 'You have lived', days, 'days' | ||
| 66 | # | ||
| 67 | age = 0 | ||
| 68 | for y in range(year, todaytuple[0] + 1): | ||
| 69 | if bdaytuple < (y, month, day) <= todaytuple: | ||
| 70 | age = age + 1 | ||
| 71 | # | ||
| 72 | print 'You are', age, 'years old' | ||
| 73 | # | ||
| 74 | if todaytuple[1:] == bdaytuple[1:]: | ||
| 75 | print 'Congratulations! Today is your', nth(age), 'birthday' | ||
| 76 | print 'Yesterday was your', | ||
| 77 | else: | ||
| 78 | print 'Today is your', | ||
| 79 | print nth(days - age), 'unbirthday' | ||
| 80 | |||
| 81 | def format((year, month, day)): | ||
| 82 | return '%d %s %d' % (day, calendar.month_name[month], year) | ||
| 83 | |||
| 84 | def nth(n): | ||
| 85 | if n == 1: return '1st' | ||
| 86 | if n == 2: return '2nd' | ||
| 87 | if n == 3: return '3rd' | ||
| 88 | return '%dth' % n | ||
| 89 | |||
| 90 | def mkdate((year, month, day)): | ||
| 91 | # Januari 1st, in 0 A.D. is arbitrarily defined to be day 1, | ||
| 92 | # even though that day never actually existed and the calendar | ||
| 93 | # was different then... | ||
| 94 | days = year*365 # years, roughly | ||
| 95 | days = days + (year+3)/4 # plus leap years, roughly | ||
| 96 | days = days - (year+99)/100 # minus non-leap years every century | ||
| 97 | days = days + (year+399)/400 # plus leap years every 4 centirues | ||
| 98 | for i in range(1, month): | ||
| 99 | if i == 2 and calendar.isleap(year): | ||
| 100 | days = days + 29 | ||
| 101 | else: | ||
| 102 | days = days + calendar.mdays[i] | ||
| 103 | days = days + day | ||
| 104 | return days | ||
| 105 | |||
| 106 | if __name__ == "__main__": | ||
| 107 | main() | ||
| @@ -0,0 +1,92 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Update a bunch of files according to a script. | ||
| 4 | # The input file contains lines of the form <filename>:<lineno>:<text>, | ||
| 5 | # meaning that the given line of the given file is to be replaced | ||
| 6 | # by the given text. This is useful for performing global substitutions | ||
| 7 | # on grep output: | ||
| 8 | |||
| 9 | import os | ||
| 10 | import sys | ||
| 11 | import re | ||
| 12 | |||
| 13 | pat = '^([^: \t\n]+):([1-9][0-9]*):' | ||
| 14 | prog = re.compile(pat) | ||
| 15 | |||
| 16 | class FileObj: | ||
| 17 | def __init__(self, filename): | ||
| 18 | self.filename = filename | ||
| 19 | self.changed = 0 | ||
| 20 | try: | ||
| 21 | self.lines = open(filename, 'r').readlines() | ||
| 22 | except IOError, msg: | ||
| 23 | print '*** Can\'t open "%s":' % filename, msg | ||
| 24 | self.lines = None | ||
| 25 | return | ||
| 26 | print 'diffing', self.filename | ||
| 27 | |||
| 28 | def finish(self): | ||
| 29 | if not self.changed: | ||
| 30 | print 'no changes to', self.filename | ||
| 31 | return | ||
| 32 | try: | ||
| 33 | os.rename(self.filename, self.filename + '~') | ||
| 34 | fp = open(self.filename, 'w') | ||
| 35 | except (os.error, IOError), msg: | ||
| 36 | print '*** Can\'t rewrite "%s":' % self.filename, msg | ||
| 37 | return | ||
| 38 | print 'writing', self.filename | ||
| 39 | for line in self.lines: | ||
| 40 | fp.write(line) | ||
| 41 | fp.close() | ||
| 42 | self.changed = 0 | ||
| 43 | |||
| 44 | def process(self, lineno, rest): | ||
| 45 | if self.lines is None: | ||
| 46 | print '(not processed): %s:%s:%s' % ( | ||
| 47 | self.filename, lineno, rest), | ||
| 48 | return | ||
| 49 | i = eval(lineno) - 1 | ||
| 50 | if not 0 <= i < len(self.lines): | ||
| 51 | print '*** Line number out of range: %s:%s:%s' % ( | ||
| 52 | self.filename, lineno, rest), | ||
| 53 | return | ||
| 54 | if self.lines[i] == rest: | ||
| 55 | print '(no change): %s:%s:%s' % ( | ||
| 56 | self.filename, lineno, rest), | ||
| 57 | return | ||
| 58 | if not self.changed: | ||
| 59 | self.changed = 1 | ||
| 60 | print '%sc%s' % (lineno, lineno) | ||
| 61 | print '<', self.lines[i], | ||
| 62 | print '---' | ||
| 63 | self.lines[i] = rest | ||
| 64 | print '>', self.lines[i], | ||
| 65 | |||
| 66 | def main(): | ||
| 67 | if sys.argv[1:]: | ||
| 68 | try: | ||
| 69 | fp = open(sys.argv[1], 'r') | ||
| 70 | except IOError, msg: | ||
| 71 | print 'Can\'t open "%s":' % sys.argv[1], msg | ||
| 72 | sys.exit(1) | ||
| 73 | else: | ||
| 74 | fp = sys.stdin | ||
| 75 | curfile = None | ||
| 76 | while 1: | ||
| 77 | line = fp.readline() | ||
| 78 | if not line: | ||
| 79 | if curfile: curfile.finish() | ||
| 80 | break | ||
| 81 | n = prog.match(line) | ||
| 82 | if n < 0: | ||
| 83 | print 'Funny line:', line, | ||
| 84 | continue | ||
| 85 | filename, lineno = prog.group(1, 2) | ||
| 86 | if not curfile or filename <> curfile.filename: | ||
| 87 | if curfile: curfile.finish() | ||
| 88 | curfile = FileObj(filename) | ||
| 89 | curfile.process(lineno, line[n:]) | ||
| 90 | |||
| 91 | if __name__ == "__main__": | ||
| 92 | main() | ||
| @@ -0,0 +1,2 @@ | |||
| 1 | # This is here so I can use 'wh' instead of 'which' in '~/bin/generic_python' | ||
| 2 | import which | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | This directory contains some demonstrations of the socket module: | ||
| 2 | |||
| 3 | broadcast.py Broadcast the time to radio.py. | ||
| 4 | echosvr.py About the simplest TCP server possible. | ||
| 5 | finger.py Client for the 'finger' protocol. | ||
| 6 | ftp.py A very simple ftp client. | ||
| 7 | gopher.py A simple gopher client. | ||
| 8 | radio.py Receive time broadcasts from broadcast.py. | ||
| 9 | telnet.py Client for the 'telnet' protocol. | ||
| 10 | throughput.py Client and server to measure TCP throughput. | ||
| 11 | unixclient.py Unix socket example, client side | ||
| 12 | unixserver.py Unix socket example, server side | ||
| 13 | udpecho.py Client and server for the UDP echo protocol. | ||
| 14 | |||
| 15 | The following file is only relevant on SGI machines (or other systems | ||
| 16 | that support multicast): | ||
| 17 | |||
| 18 | mcast.py A Python translation of | ||
| 19 | /usr/people/4Dgifts/examples/network/mcast.c | ||
| 20 | (Note that IN.py is in ../../lib/sgi.) | ||
| 21 | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # Send UDP broadcast packets | ||
| 2 | |||
| 3 | MYPORT = 50000 | ||
| 4 | |||
| 5 | import sys, time | ||
| 6 | from socket import * | ||
| 7 | |||
| 8 | s = socket(AF_INET, SOCK_DGRAM) | ||
| 9 | s.bind(('', 0)) | ||
| 10 | s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) | ||
| 11 | |||
| 12 | while 1: | ||
| 13 | data = repr(time.time()) + '\n' | ||
| 14 | s.sendto(data, ('<broadcast>', MYPORT)) | ||
| 15 | time.sleep(2) | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Python implementation of an 'echo' tcp server: echo all data it receives. | ||
| 4 | # | ||
| 5 | # This is the simplest possible server, servicing a single request only. | ||
| 6 | |||
| 7 | import sys | ||
| 8 | from socket import * | ||
| 9 | |||
| 10 | # The standard echo port isn't very useful, it requires root permissions! | ||
| 11 | # ECHO_PORT = 7 | ||
| 12 | ECHO_PORT = 50000 + 7 | ||
| 13 | BUFSIZE = 1024 | ||
| 14 | |||
| 15 | def main(): | ||
| 16 | if len(sys.argv) > 1: | ||
| 17 | port = int(eval(sys.argv[1])) | ||
| 18 | else: | ||
| 19 | port = ECHO_PORT | ||
| 20 | s = socket(AF_INET, SOCK_STREAM) | ||
| 21 | s.bind(('', port)) | ||
| 22 | s.listen(1) | ||
| 23 | conn, (remotehost, remoteport) = s.accept() | ||
| 24 | print 'connected by', remotehost, remoteport | ||
| 25 | while 1: | ||
| 26 | data = conn.recv(BUFSIZE) | ||
| 27 | if not data: | ||
| 28 | break | ||
| 29 | conn.send(data) | ||
| 30 | |||
| 31 | main() | ||
| @@ -0,0 +1,58 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Python interface to the Internet finger daemon. | ||
| 4 | # | ||
| 5 | # Usage: finger [options] [user][@host] ... | ||
| 6 | # | ||
| 7 | # If no host is given, the finger daemon on the local host is contacted. | ||
| 8 | # Options are passed uninterpreted to the finger daemon! | ||
| 9 | |||
| 10 | |||
| 11 | import sys, string | ||
| 12 | from socket import * | ||
| 13 | |||
| 14 | |||
| 15 | # Hardcode the number of the finger port here. | ||
| 16 | # It's not likely to change soon... | ||
| 17 | # | ||
| 18 | FINGER_PORT = 79 | ||
| 19 | |||
| 20 | |||
| 21 | # Function to do one remote finger invocation. | ||
| 22 | # Output goes directly to stdout (although this can be changed). | ||
| 23 | # | ||
| 24 | def finger(host, args): | ||
| 25 | s = socket(AF_INET, SOCK_STREAM) | ||
| 26 | s.connect((host, FINGER_PORT)) | ||
| 27 | s.send(args + '\n') | ||
| 28 | while 1: | ||
| 29 | buf = s.recv(1024) | ||
| 30 | if not buf: break | ||
| 31 | sys.stdout.write(buf) | ||
| 32 | sys.stdout.flush() | ||
| 33 | |||
| 34 | |||
| 35 | # Main function: argument parsing. | ||
| 36 | # | ||
| 37 | def main(): | ||
| 38 | options = '' | ||
| 39 | i = 1 | ||
| 40 | while i < len(sys.argv) and sys.argv[i][:1] == '-': | ||
| 41 | options = options + sys.argv[i] + ' ' | ||
| 42 | i = i+1 | ||
| 43 | args = sys.argv[i:] | ||
| 44 | if not args: | ||
| 45 | args = [''] | ||
| 46 | for arg in args: | ||
| 47 | if '@' in arg: | ||
| 48 | at = string.index(arg, '@') | ||
| 49 | host = arg[at+1:] | ||
| 50 | arg = arg[:at] | ||
| 51 | else: | ||
| 52 | host = '' | ||
| 53 | finger(host, options + arg) | ||
| 54 | |||
| 55 | |||
| 56 | # Call the main function. | ||
| 57 | # | ||
| 58 | main() | ||
| @@ -0,0 +1,146 @@ | |||
| 1 | # A simple FTP client. | ||
| 2 | # | ||
| 3 | # The information to write this program was gathered from RFC 959, | ||
| 4 | # but this is not a complete implementation! Yet it shows how a simple | ||
| 5 | # FTP client can be built, and you are welcome to extend it to suit | ||
| 6 | # it to your needs... | ||
| 7 | # | ||
| 8 | # How it works (assuming you've read the RFC): | ||
| 9 | # | ||
| 10 | # User commands are passed uninterpreted to the server. However, the | ||
| 11 | # user never needs to send a PORT command. Rather, the client opens a | ||
| 12 | # port right away and sends the appropriate PORT command to the server. | ||
| 13 | # When a response code 150 is received, this port is used to receive | ||
| 14 | # the data (which is written to stdout in this version), and when the | ||
| 15 | # data is exhausted, a new port is opened and a corresponding PORT | ||
| 16 | # command sent. In order to avoid errors when reusing ports quickly | ||
| 17 | # (and because there is no s.getsockname() method in Python yet) we | ||
| 18 | # cycle through a number of ports in the 50000 range. | ||
| 19 | |||
| 20 | |||
| 21 | import sys, posix, string | ||
| 22 | from socket import * | ||
| 23 | |||
| 24 | |||
| 25 | BUFSIZE = 1024 | ||
| 26 | |||
| 27 | # Default port numbers used by the FTP protocol. | ||
| 28 | # | ||
| 29 | FTP_PORT = 21 | ||
| 30 | FTP_DATA_PORT = FTP_PORT - 1 | ||
| 31 | |||
| 32 | # Change the data port to something not needing root permissions. | ||
| 33 | # | ||
| 34 | FTP_DATA_PORT = FTP_DATA_PORT + 50000 | ||
| 35 | |||
| 36 | |||
| 37 | # Main program (called at the end of this file). | ||
| 38 | # | ||
| 39 | def main(): | ||
| 40 | hostname = sys.argv[1] | ||
| 41 | control(hostname) | ||
| 42 | |||
| 43 | |||
| 44 | # Control process (user interface and user protocol interpreter). | ||
| 45 | # | ||
| 46 | def control(hostname): | ||
| 47 | # | ||
| 48 | # Create control connection | ||
| 49 | # | ||
| 50 | s = socket(AF_INET, SOCK_STREAM) | ||
| 51 | s.connect((hostname, FTP_PORT)) | ||
| 52 | f = s.makefile('r') # Reading the replies is easier from a file... | ||
| 53 | # | ||
| 54 | # Control loop | ||
| 55 | # | ||
| 56 | r = None | ||
| 57 | while 1: | ||
| 58 | code = getreply(f) | ||
| 59 | if code in ('221', 'EOF'): break | ||
| 60 | if code == '150': | ||
| 61 | getdata(r) | ||
| 62 | code = getreply(f) | ||
| 63 | r = None | ||
| 64 | if not r: | ||
| 65 | r = newdataport(s, f) | ||
| 66 | cmd = getcommand() | ||
| 67 | if not cmd: break | ||
| 68 | s.send(cmd + '\r\n') | ||
| 69 | |||
| 70 | |||
| 71 | # Create a new data port and send a PORT command to the server for it. | ||
| 72 | # (Cycle through a number of ports to avoid problems with reusing | ||
| 73 | # a port within a short time.) | ||
| 74 | # | ||
| 75 | nextport = 0 | ||
| 76 | # | ||
| 77 | def newdataport(s, f): | ||
| 78 | global nextport | ||
| 79 | port = nextport + FTP_DATA_PORT | ||
| 80 | nextport = (nextport+1) % 16 | ||
| 81 | r = socket(AF_INET, SOCK_STREAM) | ||
| 82 | r.bind((gethostbyname(gethostname()), port)) | ||
| 83 | r.listen(1) | ||
| 84 | sendportcmd(s, f, port) | ||
| 85 | return r | ||
| 86 | |||
| 87 | |||
| 88 | # Send an appropriate port command. | ||
| 89 | # | ||
| 90 | def sendportcmd(s, f, port): | ||
| 91 | hostname = gethostname() | ||
| 92 | hostaddr = gethostbyname(hostname) | ||
| 93 | hbytes = string.splitfields(hostaddr, '.') | ||
| 94 | pbytes = [repr(port/256), repr(port%256)] | ||
| 95 | bytes = hbytes + pbytes | ||
| 96 | cmd = 'PORT ' + string.joinfields(bytes, ',') | ||
| 97 | s.send(cmd + '\r\n') | ||
| 98 | code = getreply(f) | ||
| 99 | |||
| 100 | |||
| 101 | # Process an ftp reply and return the 3-digit reply code (as a string). | ||
| 102 | # The reply should be a line of text starting with a 3-digit number. | ||
| 103 | # If the 4th char is '-', it is a multi-line reply and is | ||
| 104 | # terminate by a line starting with the same 3-digit number. | ||
| 105 | # Any text while receiving the reply is echoed to the file. | ||
| 106 | # | ||
| 107 | def getreply(f): | ||
| 108 | line = f.readline() | ||
| 109 | if not line: return 'EOF' | ||
| 110 | print line, | ||
| 111 | code = line[:3] | ||
| 112 | if line[3:4] == '-': | ||
| 113 | while 1: | ||
| 114 | line = f.readline() | ||
| 115 | if not line: break # Really an error | ||
| 116 | print line, | ||
| 117 | if line[:3] == code and line[3:4] != '-': break | ||
| 118 | return code | ||
| 119 | |||
| 120 | |||
| 121 | # Get the data from the data connection. | ||
| 122 | # | ||
| 123 | def getdata(r): | ||
| 124 | print '(accepting data connection)' | ||
| 125 | conn, host = r.accept() | ||
| 126 | print '(data connection accepted)' | ||
| 127 | while 1: | ||
| 128 | data = conn.recv(BUFSIZE) | ||
| 129 | if not data: break | ||
| 130 | sys.stdout.write(data) | ||
| 131 | print '(end of data connection)' | ||
| 132 | |||
| 133 | # Get a command from the user. | ||
| 134 | # | ||
| 135 | def getcommand(): | ||
| 136 | try: | ||
| 137 | while 1: | ||
| 138 | line = raw_input('ftp.py> ') | ||
| 139 | if line: return line | ||
| 140 | except EOFError: | ||
| 141 | return '' | ||
| 142 | |||
| 143 | |||
| 144 | # Call the main program. | ||
| 145 | # | ||
| 146 | main() | ||
| @@ -0,0 +1,347 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # A simple gopher client. | ||
| 4 | # | ||
| 5 | # Usage: gopher [ [selector] host [port] ] | ||
| 6 | |||
| 7 | import string | ||
| 8 | import sys | ||
| 9 | import os | ||
| 10 | import socket | ||
| 11 | |||
| 12 | # Default selector, host and port | ||
| 13 | DEF_SELECTOR = '' | ||
| 14 | DEF_HOST = 'gopher.micro.umn.edu' | ||
| 15 | DEF_PORT = 70 | ||
| 16 | |||
| 17 | # Recognized file types | ||
| 18 | T_TEXTFILE = '0' | ||
| 19 | T_MENU = '1' | ||
| 20 | T_CSO = '2' | ||
| 21 | T_ERROR = '3' | ||
| 22 | T_BINHEX = '4' | ||
| 23 | T_DOS = '5' | ||
| 24 | T_UUENCODE = '6' | ||
| 25 | T_SEARCH = '7' | ||
| 26 | T_TELNET = '8' | ||
| 27 | T_BINARY = '9' | ||
| 28 | T_REDUNDANT = '+' | ||
| 29 | T_SOUND = 's' | ||
| 30 | |||
| 31 | # Dictionary mapping types to strings | ||
| 32 | typename = {'0': '<TEXT>', '1': '<DIR>', '2': '<CSO>', '3': '<ERROR>', \ | ||
| 33 | '4': '<BINHEX>', '5': '<DOS>', '6': '<UUENCODE>', '7': '<SEARCH>', \ | ||
| 34 | '8': '<TELNET>', '9': '<BINARY>', '+': '<REDUNDANT>', 's': '<SOUND>'} | ||
| 35 | |||
| 36 | # Oft-used characters and strings | ||
| 37 | CRLF = '\r\n' | ||
| 38 | TAB = '\t' | ||
| 39 | |||
| 40 | # Open a TCP connection to a given host and port | ||
| 41 | def open_socket(host, port): | ||
| 42 | if not port: | ||
| 43 | port = DEF_PORT | ||
| 44 | elif type(port) == type(''): | ||
| 45 | port = string.atoi(port) | ||
| 46 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 47 | s.connect((host, port)) | ||
| 48 | return s | ||
| 49 | |||
| 50 | # Send a selector to a given host and port, return a file with the reply | ||
| 51 | def send_request(selector, host, port): | ||
| 52 | s = open_socket(host, port) | ||
| 53 | s.send(selector + CRLF) | ||
| 54 | s.shutdown(1) | ||
| 55 | return s.makefile('r') | ||
| 56 | |||
| 57 | # Get a menu in the form of a list of entries | ||
| 58 | def get_menu(selector, host, port): | ||
| 59 | f = send_request(selector, host, port) | ||
| 60 | list = [] | ||
| 61 | while 1: | ||
| 62 | line = f.readline() | ||
| 63 | if not line: | ||
| 64 | print '(Unexpected EOF from server)' | ||
| 65 | break | ||
| 66 | if line[-2:] == CRLF: | ||
| 67 | line = line[:-2] | ||
| 68 | elif line[-1:] in CRLF: | ||
| 69 | line = line[:-1] | ||
| 70 | if line == '.': | ||
| 71 | break | ||
| 72 | if not line: | ||
| 73 | print '(Empty line from server)' | ||
| 74 | continue | ||
| 75 | typechar = line[0] | ||
| 76 | parts = string.splitfields(line[1:], TAB) | ||
| 77 | if len(parts) < 4: | ||
| 78 | print '(Bad line from server: %r)' % (line,) | ||
| 79 | continue | ||
| 80 | if len(parts) > 4: | ||
| 81 | print '(Extra info from server: %r)' % (parts[4:],) | ||
| 82 | parts.insert(0, typechar) | ||
| 83 | list.append(parts) | ||
| 84 | f.close() | ||
| 85 | return list | ||
| 86 | |||
| 87 | # Get a text file as a list of lines, with trailing CRLF stripped | ||
| 88 | def get_textfile(selector, host, port): | ||
| 89 | list = [] | ||
| 90 | get_alt_textfile(selector, host, port, list.append) | ||
| 91 | return list | ||
| 92 | |||
| 93 | # Get a text file and pass each line to a function, with trailing CRLF stripped | ||
| 94 | def get_alt_textfile(selector, host, port, func): | ||
| 95 | f = send_request(selector, host, port) | ||
| 96 | while 1: | ||
| 97 | line = f.readline() | ||
| 98 | if not line: | ||
| 99 | print '(Unexpected EOF from server)' | ||
| 100 | break | ||
| 101 | if line[-2:] == CRLF: | ||
| 102 | line = line[:-2] | ||
| 103 | elif line[-1:] in CRLF: | ||
| 104 | line = line[:-1] | ||
| 105 | if line == '.': | ||
| 106 | break | ||
| 107 | if line[:2] == '..': | ||
| 108 | line = line[1:] | ||
| 109 | func(line) | ||
| 110 | f.close() | ||
| 111 | |||
| 112 | # Get a binary file as one solid data block | ||
| 113 | def get_binary(selector, host, port): | ||
| 114 | f = send_request(selector, host, port) | ||
| 115 | data = f.read() | ||
| 116 | f.close() | ||
| 117 | return data | ||
| 118 | |||
| 119 | # Get a binary file and pass each block to a function | ||
| 120 | def get_alt_binary(selector, host, port, func, blocksize): | ||
| 121 | f = send_request(selector, host, port) | ||
| 122 | while 1: | ||
| 123 | data = f.read(blocksize) | ||
| 124 | if not data: | ||
| 125 | break | ||
| 126 | func(data) | ||
| 127 | |||
| 128 | # A *very* simple interactive browser | ||
| 129 | |||
| 130 | # Browser main command, has default arguments | ||
| 131 | def browser(*args): | ||
| 132 | selector = DEF_SELECTOR | ||
| 133 | host = DEF_HOST | ||
| 134 | port = DEF_PORT | ||
| 135 | n = len(args) | ||
| 136 | if n > 0 and args[0]: | ||
| 137 | selector = args[0] | ||
| 138 | if n > 1 and args[1]: | ||
| 139 | host = args[1] | ||
| 140 | if n > 2 and args[2]: | ||
| 141 | port = args[2] | ||
| 142 | if n > 3: | ||
| 143 | raise RuntimeError, 'too many args' | ||
| 144 | try: | ||
| 145 | browse_menu(selector, host, port) | ||
| 146 | except socket.error, msg: | ||
| 147 | print 'Socket error:', msg | ||
| 148 | sys.exit(1) | ||
| 149 | except KeyboardInterrupt: | ||
| 150 | print '\n[Goodbye]' | ||
| 151 | |||
| 152 | # Browse a menu | ||
| 153 | def browse_menu(selector, host, port): | ||
| 154 | list = get_menu(selector, host, port) | ||
| 155 | while 1: | ||
| 156 | print '----- MENU -----' | ||
| 157 | print 'Selector:', repr(selector) | ||
| 158 | print 'Host:', host, ' Port:', port | ||
| 159 | |||
| 160 | for i in range(len(list)): | ||
| 161 | item = list[i] | ||
| 162 | typechar, description = item[0], item[1] | ||
| 163 | print string.rjust(repr(i+1), 3) + ':', description, | ||
| 164 | if typename.has_key(typechar): | ||
| 165 | print typename[typechar] | ||
| 166 | else: | ||
| 167 | print '<TYPE=' + repr(typechar) + '>' | ||
| 168 | |||
| 169 | while 1: | ||
| 170 | try: | ||
| 171 | str = raw_input('Choice [CR == up a level]: ') | ||
| 172 | except EOFError: | ||
| 173 | |||
| 174 | return | ||
| 175 | if not str: | ||
| 176 | return | ||
| 177 | try: | ||
| 178 | choice = string.atoi(str) | ||
| 179 | except string.atoi_error: | ||
| 180 | print 'Choice must be a number; try again:' | ||
| 181 | continue | ||
| 182 | if not 0 < choice <= len(list): | ||
| 183 | print 'Choice out of range; try again:' | ||
| 184 | continue | ||
| 185 | break | ||
| 186 | item = list[choice-1] | ||
| 187 | typechar = item[0] | ||
| 188 | [i_selector, i_host, i_port] = item[2:5] | ||
| 189 | if typebrowser.has_key(typechar): | ||
| 190 | browserfunc = typebrowser[typechar] | ||
| 191 | try: | ||
| 192 | browserfunc(i_selector, i_host, i_port) | ||
| 193 | except (IOError, socket.error): | ||
| 194 | print '***', sys.exc_type, ':', sys.exc_value | ||
| 195 | else: | ||
| 196 | print 'Unsupported object type' | ||
| 197 | |||
| 198 | # Browse a text file | ||
| 199 | def browse_textfile(selector, host, port): | ||
| 200 | x = None | ||
| 201 | try: | ||
| 202 | p = os.popen('${PAGER-more}', 'w') | ||
| 203 | x = SaveLines(p) | ||
| 204 | get_alt_textfile(selector, host, port, x.writeln) | ||
| 205 | except IOError, msg: | ||
| 206 | print 'IOError:', msg | ||
| 207 | if x: | ||
| 208 | x.close() | ||
| 209 | f = open_savefile() | ||
| 210 | if not f: | ||
| 211 | return | ||
| 212 | x = SaveLines(f) | ||
| 213 | try: | ||
| 214 | get_alt_textfile(selector, host, port, x.writeln) | ||
| 215 | print 'Done.' | ||
| 216 | except IOError, msg: | ||
| 217 | print 'IOError:', msg | ||
| 218 | x.close() | ||
| 219 | |||
| 220 | # Browse a search index | ||
| 221 | def browse_search(selector, host, port): | ||
| 222 | while 1: | ||
| 223 | print '----- SEARCH -----' | ||
| 224 | print 'Selector:', repr(selector) | ||
| 225 | print 'Host:', host, ' Port:', port | ||
| 226 | |||
| 227 | try: | ||
| 228 | query = raw_input('Query [CR == up a level]: ') | ||
| 229 | except EOFError: | ||
| 230 | |||
| 231 | break | ||
| 232 | query = string.strip(query) | ||
| 233 | if not query: | ||
| 234 | break | ||
| 235 | if '\t' in query: | ||
| 236 | print 'Sorry, queries cannot contain tabs' | ||
| 237 | continue | ||
| 238 | browse_menu(selector + TAB + query, host, port) | ||
| 239 | |||
| 240 | # "Browse" telnet-based information, i.e. open a telnet session | ||
| 241 | def browse_telnet(selector, host, port): | ||
| 242 | if selector: | ||
| 243 | print 'Log in as', repr(selector) | ||
| 244 | if type(port) <> type(''): | ||
| 245 | port = repr(port) | ||
| 246 | sts = os.system('set -x; exec telnet ' + host + ' ' + port) | ||
| 247 | if sts: | ||
| 248 | print 'Exit status:', sts | ||
| 249 | |||
| 250 | # "Browse" a binary file, i.e. save it to a file | ||
| 251 | def browse_binary(selector, host, port): | ||
| 252 | f = open_savefile() | ||
| 253 | if not f: | ||
| 254 | return | ||
| 255 | x = SaveWithProgress(f) | ||
| 256 | get_alt_binary(selector, host, port, x.write, 8*1024) | ||
| 257 | x.close() | ||
| 258 | |||
| 259 | # "Browse" a sound file, i.e. play it or save it | ||
| 260 | def browse_sound(selector, host, port): | ||
| 261 | browse_binary(selector, host, port) | ||
| 262 | |||
| 263 | # Dictionary mapping types to browser functions | ||
| 264 | typebrowser = {'0': browse_textfile, '1': browse_menu, \ | ||
| 265 | '4': browse_binary, '5': browse_binary, '6': browse_textfile, \ | ||
| 266 | '7': browse_search, \ | ||
| 267 | '8': browse_telnet, '9': browse_binary, 's': browse_sound} | ||
| 268 | |||
| 269 | # Class used to save lines, appending a newline to each line | ||
| 270 | class SaveLines: | ||
| 271 | def __init__(self, f): | ||
| 272 | self.f = f | ||
| 273 | def writeln(self, line): | ||
| 274 | self.f.write(line + '\n') | ||
| 275 | def close(self): | ||
| 276 | sts = self.f.close() | ||
| 277 | if sts: | ||
| 278 | print 'Exit status:', sts | ||
| 279 | |||
| 280 | # Class used to save data while showing progress | ||
| 281 | class SaveWithProgress: | ||
| 282 | def __init__(self, f): | ||
| 283 | self.f = f | ||
| 284 | def write(self, data): | ||
| 285 | sys.stdout.write('#') | ||
| 286 | sys.stdout.flush() | ||
| 287 | self.f.write(data) | ||
| 288 | def close(self): | ||
| 289 | |||
| 290 | sts = self.f.close() | ||
| 291 | if sts: | ||
| 292 | print 'Exit status:', sts | ||
| 293 | |||
| 294 | # Ask for and open a save file, or return None if not to save | ||
| 295 | def open_savefile(): | ||
| 296 | try: | ||
| 297 | savefile = raw_input( \ | ||
| 298 | 'Save as file [CR == don\'t save; |pipeline or ~user/... OK]: ') | ||
| 299 | except EOFError: | ||
| 300 | |||
| 301 | return None | ||
| 302 | savefile = string.strip(savefile) | ||
| 303 | if not savefile: | ||
| 304 | return None | ||
| 305 | if savefile[0] == '|': | ||
| 306 | cmd = string.strip(savefile[1:]) | ||
| 307 | try: | ||
| 308 | p = os.popen(cmd, 'w') | ||
| 309 | except IOError, msg: | ||
| 310 | print repr(cmd), ':', msg | ||
| 311 | return None | ||
| 312 | print 'Piping through', repr(cmd), '...' | ||
| 313 | return p | ||
| 314 | if savefile[0] == '~': | ||
| 315 | savefile = os.path.expanduser(savefile) | ||
| 316 | try: | ||
| 317 | f = open(savefile, 'w') | ||
| 318 | except IOError, msg: | ||
| 319 | print repr(savefile), ':', msg | ||
| 320 | return None | ||
| 321 | print 'Saving to', repr(savefile), '...' | ||
| 322 | return f | ||
| 323 | |||
| 324 | # Test program | ||
| 325 | def test(): | ||
| 326 | if sys.argv[4:]: | ||
| 327 | print 'usage: gopher [ [selector] host [port] ]' | ||
| 328 | sys.exit(2) | ||
| 329 | elif sys.argv[3:]: | ||
| 330 | browser(sys.argv[1], sys.argv[2], sys.argv[3]) | ||
| 331 | elif sys.argv[2:]: | ||
| 332 | try: | ||
| 333 | port = string.atoi(sys.argv[2]) | ||
| 334 | selector = '' | ||
| 335 | host = sys.argv[1] | ||
| 336 | except string.atoi_error: | ||
| 337 | selector = sys.argv[1] | ||
| 338 | host = sys.argv[2] | ||
| 339 | port = '' | ||
| 340 | browser(selector, host, port) | ||
| 341 | elif sys.argv[1:]: | ||
| 342 | browser('', sys.argv[1]) | ||
| 343 | else: | ||
| 344 | browser() | ||
| 345 | |||
| 346 | # Call the test program as a main program | ||
| 347 | test() | ||
| @@ -0,0 +1,93 @@ | |||
| 1 | # Send/receive UDP multicast packets. | ||
| 2 | # Requires that your OS kernel supports IP multicast. | ||
| 3 | # This is built-in on SGI, still optional for most other vendors. | ||
| 4 | # | ||
| 5 | # Usage: | ||
| 6 | # mcast -s (sender) | ||
| 7 | # mcast -b (sender, using broadcast instead multicast) | ||
| 8 | # mcast (receivers) | ||
| 9 | |||
| 10 | MYPORT = 8123 | ||
| 11 | MYGROUP = '225.0.0.250' | ||
| 12 | |||
| 13 | import sys | ||
| 14 | import time | ||
| 15 | import struct | ||
| 16 | from socket import * | ||
| 17 | |||
| 18 | |||
| 19 | # Main program | ||
| 20 | def main(): | ||
| 21 | flags = sys.argv[1:] | ||
| 22 | # | ||
| 23 | if flags: | ||
| 24 | sender(flags[0]) | ||
| 25 | else: | ||
| 26 | receiver() | ||
| 27 | |||
| 28 | |||
| 29 | # Sender subroutine (only one per local area network) | ||
| 30 | def sender(flag): | ||
| 31 | s = socket(AF_INET, SOCK_DGRAM) | ||
| 32 | if flag == '-b': | ||
| 33 | s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) | ||
| 34 | mygroup = '<broadcast>' | ||
| 35 | else: | ||
| 36 | mygroup = MYGROUP | ||
| 37 | ttl = struct.pack('b', 1) # Time-to-live | ||
| 38 | s.setsockopt(IPPROTO_IP, IP_MULTICAST_TTL, ttl) | ||
| 39 | while 1: | ||
| 40 | data = repr(time.time()) | ||
| 41 | ## data = data + (1400 - len(data)) * '\0' | ||
| 42 | s.sendto(data, (mygroup, MYPORT)) | ||
| 43 | time.sleep(1) | ||
| 44 | |||
| 45 | |||
| 46 | # Receiver subroutine (as many as you like) | ||
| 47 | def receiver(): | ||
| 48 | # Open and initialize the socket | ||
| 49 | s = openmcastsock(MYGROUP, MYPORT) | ||
| 50 | # | ||
| 51 | # Loop, printing any data we receive | ||
| 52 | while 1: | ||
| 53 | data, sender = s.recvfrom(1500) | ||
| 54 | while data[-1:] == '\0': data = data[:-1] # Strip trailing \0's | ||
| 55 | print sender, ':', repr(data) | ||
| 56 | |||
| 57 | |||
| 58 | # Open a UDP socket, bind it to a port and select a multicast group | ||
| 59 | def openmcastsock(group, port): | ||
| 60 | # Import modules used only here | ||
| 61 | import string | ||
| 62 | import struct | ||
| 63 | # | ||
| 64 | # Create a socket | ||
| 65 | s = socket(AF_INET, SOCK_DGRAM) | ||
| 66 | # | ||
| 67 | # Allow multiple copies of this program on one machine | ||
| 68 | # (not strictly needed) | ||
| 69 | s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) | ||
| 70 | # | ||
| 71 | # Bind it to the port | ||
| 72 | s.bind(('', port)) | ||
| 73 | # | ||
| 74 | # Look up multicast group address in name server | ||
| 75 | # (doesn't hurt if it is already in ddd.ddd.ddd.ddd format) | ||
| 76 | group = gethostbyname(group) | ||
| 77 | # | ||
| 78 | # Construct binary group address | ||
| 79 | bytes = map(int, string.split(group, ".")) | ||
| 80 | grpaddr = 0 | ||
| 81 | for byte in bytes: grpaddr = (grpaddr << 8) | byte | ||
| 82 | # | ||
| 83 | # Construct struct mreq from grpaddr and ifaddr | ||
| 84 | ifaddr = INADDR_ANY | ||
| 85 | mreq = struct.pack('ll', htonl(grpaddr), htonl(ifaddr)) | ||
| 86 | # | ||
| 87 | # Add group membership | ||
| 88 | s.setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, mreq) | ||
| 89 | # | ||
| 90 | return s | ||
| 91 | |||
| 92 | |||
| 93 | main() | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | # Receive UDP packets transmitted by a broadcasting service | ||
| 2 | |||
| 3 | MYPORT = 50000 | ||
| 4 | |||
| 5 | import sys | ||
| 6 | from socket import * | ||
| 7 | |||
| 8 | s = socket(AF_INET, SOCK_DGRAM) | ||
| 9 | s.bind(('', MYPORT)) | ||
| 10 | |||
| 11 | while 1: | ||
| 12 | data, wherefrom = s.recvfrom(1500, 0) | ||
| 13 | sys.stderr.write(repr(wherefrom) + '\n') | ||
| 14 | sys.stdout.write(data) | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Remote python client. | ||
| 4 | # Execute Python commands remotely and send output back. | ||
| 5 | |||
| 6 | import sys | ||
| 7 | import string | ||
| 8 | from socket import * | ||
| 9 | |||
| 10 | PORT = 4127 | ||
| 11 | BUFSIZE = 1024 | ||
| 12 | |||
| 13 | def main(): | ||
| 14 | if len(sys.argv) < 3: | ||
| 15 | print "usage: rpython host command" | ||
| 16 | sys.exit(2) | ||
| 17 | host = sys.argv[1] | ||
| 18 | port = PORT | ||
| 19 | i = string.find(host, ':') | ||
| 20 | if i >= 0: | ||
| 21 | port = string.atoi(port[i+1:]) | ||
| 22 | host = host[:i] | ||
| 23 | command = string.join(sys.argv[2:]) | ||
| 24 | s = socket(AF_INET, SOCK_STREAM) | ||
| 25 | s.connect((host, port)) | ||
| 26 | s.send(command) | ||
| 27 | s.shutdown(1) | ||
| 28 | reply = '' | ||
| 29 | while 1: | ||
| 30 | data = s.recv(BUFSIZE) | ||
| 31 | if not data: break | ||
| 32 | reply = reply + data | ||
| 33 | print reply, | ||
| 34 | |||
| 35 | main() | ||
| @@ -0,0 +1,52 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Remote python server. | ||
| 4 | # Execute Python commands remotely and send output back. | ||
| 5 | # WARNING: This version has a gaping security hole -- it accepts requests | ||
| 6 | # from any host on the Internet! | ||
| 7 | |||
| 8 | import sys | ||
| 9 | from socket import * | ||
| 10 | import StringIO | ||
| 11 | import traceback | ||
| 12 | |||
| 13 | PORT = 4127 | ||
| 14 | BUFSIZE = 1024 | ||
| 15 | |||
| 16 | def main(): | ||
| 17 | if len(sys.argv) > 1: | ||
| 18 | port = int(eval(sys.argv[1])) | ||
| 19 | else: | ||
| 20 | port = PORT | ||
| 21 | s = socket(AF_INET, SOCK_STREAM) | ||
| 22 | s.bind(('', port)) | ||
| 23 | s.listen(1) | ||
| 24 | while 1: | ||
| 25 | conn, (remotehost, remoteport) = s.accept() | ||
| 26 | print 'connected by', remotehost, remoteport | ||
| 27 | request = '' | ||
| 28 | while 1: | ||
| 29 | data = conn.recv(BUFSIZE) | ||
| 30 | if not data: | ||
| 31 | break | ||
| 32 | request = request + data | ||
| 33 | reply = execute(request) | ||
| 34 | conn.send(reply) | ||
| 35 | conn.close() | ||
| 36 | |||
| 37 | def execute(request): | ||
| 38 | stdout = sys.stdout | ||
| 39 | stderr = sys.stderr | ||
| 40 | sys.stdout = sys.stderr = fakefile = StringIO.StringIO() | ||
| 41 | try: | ||
| 42 | try: | ||
| 43 | exec request in {}, {} | ||
| 44 | except: | ||
| 45 | |||
| 46 | traceback.print_exc(100) | ||
| 47 | finally: | ||
| 48 | sys.stderr = stderr | ||
| 49 | sys.stdout = stdout | ||
| 50 | return fakefile.getvalue() | ||
| 51 | |||
| 52 | main() | ||
| @@ -0,0 +1,109 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Minimal interface to the Internet telnet protocol. | ||
| 4 | # | ||
| 5 | # It refuses all telnet options and does not recognize any of the other | ||
| 6 | # telnet commands, but can still be used to connect in line-by-line mode. | ||
| 7 | # It's also useful to play with a number of other services, | ||
| 8 | # like time, finger, smtp and even ftp. | ||
| 9 | # | ||
| 10 | # Usage: telnet host [port] | ||
| 11 | # | ||
| 12 | # The port may be a service name or a decimal port number; | ||
| 13 | # it defaults to 'telnet'. | ||
| 14 | |||
| 15 | |||
| 16 | import sys, posix, time | ||
| 17 | from socket import * | ||
| 18 | |||
| 19 | BUFSIZE = 1024 | ||
| 20 | |||
| 21 | # Telnet protocol characters | ||
| 22 | |||
| 23 | IAC = chr(255) # Interpret as command | ||
| 24 | DONT = chr(254) | ||
| 25 | DO = chr(253) | ||
| 26 | WONT = chr(252) | ||
| 27 | WILL = chr(251) | ||
| 28 | |||
| 29 | def main(): | ||
| 30 | host = sys.argv[1] | ||
| 31 | try: | ||
| 32 | hostaddr = gethostbyname(host) | ||
| 33 | except error: | ||
| 34 | sys.stderr.write(sys.argv[1] + ': bad host name\n') | ||
| 35 | sys.exit(2) | ||
| 36 | # | ||
| 37 | if len(sys.argv) > 2: | ||
| 38 | servname = sys.argv[2] | ||
| 39 | else: | ||
| 40 | servname = 'telnet' | ||
| 41 | # | ||
| 42 | if '0' <= servname[:1] <= '9': | ||
| 43 | port = eval(servname) | ||
| 44 | else: | ||
| 45 | try: | ||
| 46 | port = getservbyname(servname, 'tcp') | ||
| 47 | except error: | ||
| 48 | sys.stderr.write(servname + ': bad tcp service name\n') | ||
| 49 | sys.exit(2) | ||
| 50 | # | ||
| 51 | s = socket(AF_INET, SOCK_STREAM) | ||
| 52 | # | ||
| 53 | try: | ||
| 54 | s.connect((host, port)) | ||
| 55 | except error, msg: | ||
| 56 | sys.stderr.write('connect failed: ' + repr(msg) + '\n') | ||
| 57 | sys.exit(1) | ||
| 58 | # | ||
| 59 | pid = posix.fork() | ||
| 60 | # | ||
| 61 | if pid == 0: | ||
| 62 | # child -- read stdin, write socket | ||
| 63 | while 1: | ||
| 64 | line = sys.stdin.readline() | ||
| 65 | s.send(line) | ||
| 66 | else: | ||
| 67 | # parent -- read socket, write stdout | ||
| 68 | iac = 0 # Interpret next char as command | ||
| 69 | opt = '' # Interpret next char as option | ||
| 70 | while 1: | ||
| 71 | data = s.recv(BUFSIZE) | ||
| 72 | if not data: | ||
| 73 | # EOF; kill child and exit | ||
| 74 | sys.stderr.write( '(Closed by remote host)\n') | ||
| 75 | posix.kill(pid, 9) | ||
| 76 | sys.exit(1) | ||
| 77 | cleandata = '' | ||
| 78 | for c in data: | ||
| 79 | if opt: | ||
| 80 | print ord(c) | ||
| 81 | s.send(opt + c) | ||
| 82 | opt = '' | ||
| 83 | elif iac: | ||
| 84 | iac = 0 | ||
| 85 | if c == IAC: | ||
| 86 | cleandata = cleandata + c | ||
| 87 | elif c in (DO, DONT): | ||
| 88 | if c == DO: print '(DO)', | ||
| 89 | else: print '(DONT)', | ||
| 90 | opt = IAC + WONT | ||
| 91 | elif c in (WILL, WONT): | ||
| 92 | if c == WILL: print '(WILL)', | ||
| 93 | else: print '(WONT)', | ||
| 94 | opt = IAC + DONT | ||
| 95 | else: | ||
| 96 | print '(command)', ord(c) | ||
| 97 | elif c == IAC: | ||
| 98 | iac = 1 | ||
| 99 | print '(IAC)', | ||
| 100 | else: | ||
| 101 | cleandata = cleandata + c | ||
| 102 | sys.stdout.write(cleandata) | ||
| 103 | sys.stdout.flush() | ||
| 104 | |||
| 105 | |||
| 106 | try: | ||
| 107 | main() | ||
| 108 | except KeyboardInterrupt: | ||
| 109 | pass | ||
| @@ -0,0 +1,93 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Test network throughput. | ||
| 4 | # | ||
| 5 | # Usage: | ||
| 6 | # 1) on host_A: throughput -s [port] # start a server | ||
| 7 | # 2) on host_B: throughput -c count host_A [port] # start a client | ||
| 8 | # | ||
| 9 | # The server will service multiple clients until it is killed. | ||
| 10 | # | ||
| 11 | # The client performs one transfer of count*BUFSIZE bytes and | ||
| 12 | # measures the time it takes (roundtrip!). | ||
| 13 | |||
| 14 | |||
| 15 | import sys, time | ||
| 16 | from socket import * | ||
| 17 | |||
| 18 | MY_PORT = 50000 + 42 | ||
| 19 | |||
| 20 | BUFSIZE = 1024 | ||
| 21 | |||
| 22 | |||
| 23 | def main(): | ||
| 24 | if len(sys.argv) < 2: | ||
| 25 | usage() | ||
| 26 | if sys.argv[1] == '-s': | ||
| 27 | server() | ||
| 28 | elif sys.argv[1] == '-c': | ||
| 29 | client() | ||
| 30 | else: | ||
| 31 | usage() | ||
| 32 | |||
| 33 | |||
| 34 | def usage(): | ||
| 35 | sys.stdout = sys.stderr | ||
| 36 | print 'Usage: (on host_A) throughput -s [port]' | ||
| 37 | print 'and then: (on host_B) throughput -c count host_A [port]' | ||
| 38 | sys.exit(2) | ||
| 39 | |||
| 40 | |||
| 41 | def server(): | ||
| 42 | if len(sys.argv) > 2: | ||
| 43 | port = eval(sys.argv[2]) | ||
| 44 | else: | ||
| 45 | port = MY_PORT | ||
| 46 | s = socket(AF_INET, SOCK_STREAM) | ||
| 47 | s.bind(('', port)) | ||
| 48 | s.listen(1) | ||
| 49 | print 'Server ready...' | ||
| 50 | while 1: | ||
| 51 | conn, (host, remoteport) = s.accept() | ||
| 52 | while 1: | ||
| 53 | data = conn.recv(BUFSIZE) | ||
| 54 | if not data: | ||
| 55 | break | ||
| 56 | del data | ||
| 57 | conn.send('OK\n') | ||
| 58 | conn.close() | ||
| 59 | print 'Done with', host, 'port', remoteport | ||
| 60 | |||
| 61 | |||
| 62 | def client(): | ||
| 63 | if len(sys.argv) < 4: | ||
| 64 | usage() | ||
| 65 | count = int(eval(sys.argv[2])) | ||
| 66 | host = sys.argv[3] | ||
| 67 | if len(sys.argv) > 4: | ||
| 68 | port = eval(sys.argv[4]) | ||
| 69 | else: | ||
| 70 | port = MY_PORT | ||
| 71 | testdata = 'x' * (BUFSIZE-1) + '\n' | ||
| 72 | t1 = time.time() | ||
| 73 | s = socket(AF_INET, SOCK_STREAM) | ||
| 74 | t2 = time.time() | ||
| 75 | s.connect((host, port)) | ||
| 76 | t3 = time.time() | ||
| 77 | i = 0 | ||
| 78 | while i < count: | ||
| 79 | i = i+1 | ||
| 80 | s.send(testdata) | ||
| 81 | s.shutdown(1) # Send EOF | ||
| 82 | t4 = time.time() | ||
| 83 | data = s.recv(BUFSIZE) | ||
| 84 | t5 = time.time() | ||
| 85 | print data | ||
| 86 | print 'Raw timers:', t1, t2, t3, t4, t5 | ||
| 87 | print 'Intervals:', t2-t1, t3-t2, t4-t3, t5-t4 | ||
| 88 | print 'Total:', t5-t1 | ||
| 89 | print 'Throughput:', round((BUFSIZE*count*0.001) / (t5-t1), 3), | ||
| 90 | print 'K/sec.' | ||
| 91 | |||
| 92 | |||
| 93 | main() | ||
| @@ -0,0 +1,63 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Client and server for udp (datagram) echo. | ||
| 4 | # | ||
| 5 | # Usage: udpecho -s [port] (to start a server) | ||
| 6 | # or: udpecho -c host [port] <file (client) | ||
| 7 | |||
| 8 | import sys | ||
| 9 | from socket import * | ||
| 10 | |||
| 11 | ECHO_PORT = 50000 + 7 | ||
| 12 | BUFSIZE = 1024 | ||
| 13 | |||
| 14 | def main(): | ||
| 15 | if len(sys.argv) < 2: | ||
| 16 | usage() | ||
| 17 | if sys.argv[1] == '-s': | ||
| 18 | server() | ||
| 19 | elif sys.argv[1] == '-c': | ||
| 20 | client() | ||
| 21 | else: | ||
| 22 | usage() | ||
| 23 | |||
| 24 | def usage(): | ||
| 25 | sys.stdout = sys.stderr | ||
| 26 | print 'Usage: udpecho -s [port] (server)' | ||
| 27 | print 'or: udpecho -c host [port] <file (client)' | ||
| 28 | sys.exit(2) | ||
| 29 | |||
| 30 | def server(): | ||
| 31 | if len(sys.argv) > 2: | ||
| 32 | port = eval(sys.argv[2]) | ||
| 33 | else: | ||
| 34 | port = ECHO_PORT | ||
| 35 | s = socket(AF_INET, SOCK_DGRAM) | ||
| 36 | s.bind(('', port)) | ||
| 37 | print 'udp echo server ready' | ||
| 38 | while 1: | ||
| 39 | data, addr = s.recvfrom(BUFSIZE) | ||
| 40 | print 'server received %r from %r' % (data, addr) | ||
| 41 | s.sendto(data, addr) | ||
| 42 | |||
| 43 | def client(): | ||
| 44 | if len(sys.argv) < 3: | ||
| 45 | usage() | ||
| 46 | host = sys.argv[2] | ||
| 47 | if len(sys.argv) > 3: | ||
| 48 | port = eval(sys.argv[3]) | ||
| 49 | else: | ||
| 50 | port = ECHO_PORT | ||
| 51 | addr = host, port | ||
| 52 | s = socket(AF_INET, SOCK_DGRAM) | ||
| 53 | s.bind(('', 0)) | ||
| 54 | print 'udp echo client ready, reading stdin' | ||
| 55 | while 1: | ||
| 56 | line = sys.stdin.readline() | ||
| 57 | if not line: | ||
| 58 | break | ||
| 59 | s.sendto(line, addr) | ||
| 60 | data, fromaddr = s.recvfrom(BUFSIZE) | ||
| 61 | print 'client received %r from %r' % (data, fromaddr) | ||
| 62 | |||
| 63 | main() | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | # Send UDP broadcast packets | ||
| 2 | |||
| 3 | MYPORT = 50000 | ||
| 4 | |||
| 5 | import sys, time | ||
| 6 | from socket import * | ||
| 7 | |||
| 8 | s = socket(AF_INET, SOCK_DGRAM) | ||
| 9 | s.bind(('', 0)) | ||
| 10 | |||
| 11 | while 1: | ||
| 12 | data = repr(time.time()) + '\n' | ||
| 13 | s.sendto(data, ('', MYPORT)) | ||
| 14 | time.sleep(2) | ||
| @@ -0,0 +1,12 @@ | |||
| 1 | # Echo client demo using Unix sockets | ||
| 2 | # Piet van Oostrum | ||
| 3 | |||
| 4 | from socket import * | ||
| 5 | |||
| 6 | FILE = 'unix-socket' | ||
| 7 | s = socket(AF_UNIX, SOCK_STREAM) | ||
| 8 | s.connect(FILE) | ||
| 9 | s.send('Hello, world') | ||
| 10 | data = s.recv(1024) | ||
| 11 | s.close() | ||
| 12 | print 'Received', repr(data) | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | # Echo server demo using Unix sockets (handles one connection only) | ||
| 2 | # Piet van Oostrum | ||
| 3 | |||
| 4 | import os | ||
| 5 | from socket import * | ||
| 6 | |||
| 7 | FILE = 'unix-socket' | ||
| 8 | s = socket(AF_UNIX, SOCK_STREAM) | ||
| 9 | s.bind(FILE) | ||
| 10 | |||
| 11 | print 'Sock name is: ['+s.getsockname()+']' | ||
| 12 | |||
| 13 | # Wait for a connection | ||
| 14 | s.listen(1) | ||
| 15 | conn, addr = s.accept() | ||
| 16 | |||
| 17 | while True: | ||
| 18 | data = conn.recv(1024) | ||
| 19 | if not data: | ||
| 20 | break | ||
| 21 | conn.send(data) | ||
| 22 | |||
| 23 | conn.close() | ||
| 24 | os.unlink(FILE) | ||
| @@ -0,0 +1,159 @@ | |||
| 1 | # Coroutine implementation using Python threads. | ||
| 2 | # | ||
| 3 | # Combines ideas from Guido's Generator module, and from the coroutine | ||
| 4 | # features of Icon and Simula 67. | ||
| 5 | # | ||
| 6 | # To run a collection of functions as coroutines, you need to create | ||
| 7 | # a Coroutine object to control them: | ||
| 8 | # co = Coroutine() | ||
| 9 | # and then 'create' a subsidiary object for each function in the | ||
| 10 | # collection: | ||
| 11 | # cof1 = co.create(f1 [, arg1, arg2, ...]) # [] means optional, | ||
| 12 | # cof2 = co.create(f2 [, arg1, arg2, ...]) #... not list | ||
| 13 | # cof3 = co.create(f3 [, arg1, arg2, ...]) | ||
| 14 | # etc. The functions need not be distinct; 'create'ing the same | ||
| 15 | # function multiple times gives you independent instances of the | ||
| 16 | # function. | ||
| 17 | # | ||
| 18 | # To start the coroutines running, use co.tran on one of the create'd | ||
| 19 | # functions; e.g., co.tran(cof2). The routine that first executes | ||
| 20 | # co.tran is called the "main coroutine". It's special in several | ||
| 21 | # respects: it existed before you created the Coroutine object; if any of | ||
| 22 | # the create'd coroutines exits (does a return, or suffers an unhandled | ||
| 23 | # exception), EarlyExit error is raised in the main coroutine; and the | ||
| 24 | # co.detach() method transfers control directly to the main coroutine | ||
| 25 | # (you can't use co.tran() for this because the main coroutine doesn't | ||
| 26 | # have a name ...). | ||
| 27 | # | ||
| 28 | # Coroutine objects support these methods: | ||
| 29 | # | ||
| 30 | # handle = .create(func [, arg1, arg2, ...]) | ||
| 31 | # Creates a coroutine for an invocation of func(arg1, arg2, ...), | ||
| 32 | # and returns a handle ("name") for the coroutine so created. The | ||
| 33 | # handle can be used as the target in a subsequent .tran(). | ||
| 34 | # | ||
| 35 | # .tran(target, data=None) | ||
| 36 | # Transfer control to the create'd coroutine "target", optionally | ||
| 37 | # passing it an arbitrary piece of data. To the coroutine A that does | ||
| 38 | # the .tran, .tran acts like an ordinary function call: another | ||
| 39 | # coroutine B can .tran back to it later, and if it does A's .tran | ||
| 40 | # returns the 'data' argument passed to B's tran. E.g., | ||
| 41 | # | ||
| 42 | # in coroutine coA in coroutine coC in coroutine coB | ||
| 43 | # x = co.tran(coC) co.tran(coB) co.tran(coA,12) | ||
| 44 | # print x # 12 | ||
| 45 | # | ||
| 46 | # The data-passing feature is taken from Icon, and greatly cuts | ||
| 47 | # the need to use global variables for inter-coroutine communication. | ||
| 48 | # | ||
| 49 | # .back( data=None ) | ||
| 50 | # The same as .tran(invoker, data=None), where 'invoker' is the | ||
| 51 | # coroutine that most recently .tran'ed control to the coroutine | ||
| 52 | # doing the .back. This is akin to Icon's "&source". | ||
| 53 | # | ||
| 54 | # .detach( data=None ) | ||
| 55 | # The same as .tran(main, data=None), where 'main' is the | ||
| 56 | # (unnameable!) coroutine that started it all. 'main' has all the | ||
| 57 | # rights of any other coroutine: upon receiving control, it can | ||
| 58 | # .tran to an arbitrary coroutine of its choosing, go .back to | ||
| 59 | # the .detach'er, or .kill the whole thing. | ||
| 60 | # | ||
| 61 | # .kill() | ||
| 62 | # Destroy all the coroutines, and return control to the main | ||
| 63 | # coroutine. None of the create'ed coroutines can be resumed after a | ||
| 64 | # .kill(). An EarlyExit exception does a .kill() automatically. It's | ||
| 65 | # a good idea to .kill() coroutines you're done with, since the | ||
| 66 | # current implementation consumes a thread for each coroutine that | ||
| 67 | # may be resumed. | ||
| 68 | |||
| 69 | import thread | ||
| 70 | import sync | ||
| 71 | |||
| 72 | class _CoEvent: | ||
| 73 | def __init__(self, func): | ||
| 74 | self.f = func | ||
| 75 | self.e = sync.event() | ||
| 76 | |||
| 77 | def __repr__(self): | ||
| 78 | if self.f is None: | ||
| 79 | return 'main coroutine' | ||
| 80 | else: | ||
| 81 | return 'coroutine for func ' + self.f.func_name | ||
| 82 | |||
| 83 | def __hash__(self): | ||
| 84 | return id(self) | ||
| 85 | |||
| 86 | def __cmp__(x,y): | ||
| 87 | return cmp(id(x), id(y)) | ||
| 88 | |||
| 89 | def resume(self): | ||
| 90 | self.e.post() | ||
| 91 | |||
| 92 | def wait(self): | ||
| 93 | self.e.wait() | ||
| 94 | self.e.clear() | ||
| 95 | |||
| 96 | Killed = 'Coroutine.Killed' | ||
| 97 | EarlyExit = 'Coroutine.EarlyExit' | ||
| 98 | |||
| 99 | class Coroutine: | ||
| 100 | def __init__(self): | ||
| 101 | self.active = self.main = _CoEvent(None) | ||
| 102 | self.invokedby = {self.main: None} | ||
| 103 | self.killed = 0 | ||
| 104 | self.value = None | ||
| 105 | self.terminated_by = None | ||
| 106 | |||
| 107 | def create(self, func, *args): | ||
| 108 | me = _CoEvent(func) | ||
| 109 | self.invokedby[me] = None | ||
| 110 | thread.start_new_thread(self._start, (me,) + args) | ||
| 111 | return me | ||
| 112 | |||
| 113 | def _start(self, me, *args): | ||
| 114 | me.wait() | ||
| 115 | if not self.killed: | ||
| 116 | try: | ||
| 117 | try: | ||
| 118 | apply(me.f, args) | ||
| 119 | except Killed: | ||
| 120 | pass | ||
| 121 | finally: | ||
| 122 | if not self.killed: | ||
| 123 | self.terminated_by = me | ||
| 124 | self.kill() | ||
| 125 | |||
| 126 | def kill(self): | ||
| 127 | if self.killed: | ||
| 128 | raise TypeError, 'kill() called on dead coroutines' | ||
| 129 | self.killed = 1 | ||
| 130 | for coroutine in self.invokedby.keys(): | ||
| 131 | coroutine.resume() | ||
| 132 | |||
| 133 | def back(self, data=None): | ||
| 134 | return self.tran( self.invokedby[self.active], data ) | ||
| 135 | |||
| 136 | def detach(self, data=None): | ||
| 137 | return self.tran( self.main, data ) | ||
| 138 | |||
| 139 | def tran(self, target, data=None): | ||
| 140 | if not self.invokedby.has_key(target): | ||
| 141 | raise TypeError, '.tran target %r is not an active coroutine' % (target,) | ||
| 142 | if self.killed: | ||
| 143 | raise TypeError, '.tran target %r is killed' % (target,) | ||
| 144 | self.value = data | ||
| 145 | me = self.active | ||
| 146 | self.invokedby[target] = me | ||
| 147 | self.active = target | ||
| 148 | target.resume() | ||
| 149 | |||
| 150 | me.wait() | ||
| 151 | if self.killed: | ||
| 152 | if self.main is not me: | ||
| 153 | raise Killed | ||
| 154 | if self.terminated_by is not None: | ||
| 155 | raise EarlyExit, '%r terminated early' % (self.terminated_by,) | ||
| 156 | |||
| 157 | return self.value | ||
| 158 | |||
| 159 | # end of module | ||
| @@ -0,0 +1,84 @@ | |||
| 1 | # Generator implementation using threads | ||
| 2 | |||
| 3 | import thread | ||
| 4 | |||
| 5 | Killed = 'Generator.Killed' | ||
| 6 | |||
| 7 | class Generator: | ||
| 8 | # Constructor | ||
| 9 | def __init__(self, func, args): | ||
| 10 | self.getlock = thread.allocate_lock() | ||
| 11 | self.putlock = thread.allocate_lock() | ||
| 12 | self.getlock.acquire() | ||
| 13 | self.putlock.acquire() | ||
| 14 | self.func = func | ||
| 15 | self.args = args | ||
| 16 | self.done = 0 | ||
| 17 | self.killed = 0 | ||
| 18 | thread.start_new_thread(self._start, ()) | ||
| 19 | # Internal routine | ||
| 20 | def _start(self): | ||
| 21 | try: | ||
| 22 | self.putlock.acquire() | ||
| 23 | if not self.killed: | ||
| 24 | try: | ||
| 25 | apply(self.func, (self,) + self.args) | ||
| 26 | except Killed: | ||
| 27 | pass | ||
| 28 | finally: | ||
| 29 | if not self.killed: | ||
| 30 | self.done = 1 | ||
| 31 | self.getlock.release() | ||
| 32 | # Called by producer for each value; raise Killed if no more needed | ||
| 33 | def put(self, value): | ||
| 34 | if self.killed: | ||
| 35 | raise TypeError, 'put() called on killed generator' | ||
| 36 | self.value = value | ||
| 37 | self.getlock.release() # Resume consumer thread | ||
| 38 | self.putlock.acquire() # Wait for next get() call | ||
| 39 | if self.killed: | ||
| 40 | raise Killed | ||
| 41 | # Called by producer to get next value; raise EOFError if no more | ||
| 42 | def get(self): | ||
| 43 | if self.killed: | ||
| 44 | raise TypeError, 'get() called on killed generator' | ||
| 45 | self.putlock.release() # Resume producer thread | ||
| 46 | self.getlock.acquire() # Wait for value to appear | ||
| 47 | if self.done: | ||
| 48 | raise EOFError # Say there are no more values | ||
| 49 | return self.value | ||
| 50 | # Called by consumer if no more values wanted | ||
| 51 | def kill(self): | ||
| 52 | if self.killed: | ||
| 53 | raise TypeError, 'kill() called on killed generator' | ||
| 54 | self.killed = 1 | ||
| 55 | self.putlock.release() | ||
| 56 | # Clone constructor | ||
| 57 | def clone(self): | ||
| 58 | return Generator(self.func, self.args) | ||
| 59 | |||
| 60 | def pi(g): | ||
| 61 | k, a, b, a1, b1 = 2L, 4L, 1L, 12L, 4L | ||
| 62 | while 1: | ||
| 63 | # Next approximation | ||
| 64 | p, q, k = k*k, 2L*k+1L, k+1L | ||
| 65 | a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1 | ||
| 66 | # Print common digits | ||
| 67 | d, d1 = a/b, a1/b1 | ||
| 68 | while d == d1: | ||
| 69 | g.put(int(d)) | ||
| 70 | a, a1 = 10L*(a%b), 10L*(a1%b1) | ||
| 71 | d, d1 = a/b, a1/b1 | ||
| 72 | |||
| 73 | def test(): | ||
| 74 | g = Generator(pi, ()) | ||
| 75 | g.kill() | ||
| 76 | g = Generator(pi, ()) | ||
| 77 | for i in range(10): print g.get(), | ||
| 78 | |||
| 79 | h = g.clone() | ||
| 80 | g.kill() | ||
| 81 | while 1: | ||
| 82 | print h.get(), | ||
| 83 | |||
| 84 | test() | ||
| @@ -0,0 +1,13 @@ | |||
| 1 | This directory contains some demonstrations of the thread module. | ||
| 2 | |||
| 3 | These are mostly "proof of concept" type applications: | ||
| 4 | |||
| 5 | Generator.py Generator class implemented with threads. | ||
| 6 | find.py Parallelized "find(1)" (looks for directories). | ||
| 7 | sync.py Condition variables primitives by Tim Peters. | ||
| 8 | telnet.py Version of ../sockets/telnet.py using threads. | ||
| 9 | wpi.py Version of ../scripts/pi.py using threads (needs stdwin). | ||
| 10 | |||
| 11 | Coroutine.py Coroutines using threads, by Tim Peters (22 May 94) | ||
| 12 | fcmp.py Example of above, by Tim | ||
| 13 | squasher.py Another example of above, also by Tim | ||
| @@ -0,0 +1,64 @@ | |||
| 1 | # Coroutine example: controlling multiple instances of a single function | ||
| 2 | |||
| 3 | from Coroutine import * | ||
| 4 | |||
| 5 | # fringe visits a nested list in inorder, and detaches for each non-list | ||
| 6 | # element; raises EarlyExit after the list is exhausted | ||
| 7 | def fringe(co, list): | ||
| 8 | for x in list: | ||
| 9 | if type(x) is type([]): | ||
| 10 | fringe(co, x) | ||
| 11 | else: | ||
| 12 | co.back(x) | ||
| 13 | |||
| 14 | def printinorder(list): | ||
| 15 | co = Coroutine() | ||
| 16 | f = co.create(fringe, co, list) | ||
| 17 | try: | ||
| 18 | while 1: | ||
| 19 | print co.tran(f), | ||
| 20 | except EarlyExit: | ||
| 21 | pass | ||
| 22 | |||
| 23 | |||
| 24 | printinorder([1,2,3]) # 1 2 3 | ||
| 25 | printinorder([[[[1,[2]]],3]]) # ditto | ||
| 26 | x = [0, 1, [2, [3]], [4,5], [[[6]]] ] | ||
| 27 | printinorder(x) # 0 1 2 3 4 5 6 | ||
| 28 | |||
| 29 | # fcmp lexicographically compares the fringes of two nested lists | ||
| 30 | def fcmp(l1, l2): | ||
| 31 | co1 = Coroutine(); f1 = co1.create(fringe, co1, l1) | ||
| 32 | co2 = Coroutine(); f2 = co2.create(fringe, co2, l2) | ||
| 33 | while 1: | ||
| 34 | try: | ||
| 35 | v1 = co1.tran(f1) | ||
| 36 | except EarlyExit: | ||
| 37 | try: | ||
| 38 | v2 = co2.tran(f2) | ||
| 39 | except EarlyExit: | ||
| 40 | return 0 | ||
| 41 | co2.kill() | ||
| 42 | return -1 | ||
| 43 | try: | ||
| 44 | v2 = co2.tran(f2) | ||
| 45 | except EarlyExit: | ||
| 46 | co1.kill() | ||
| 47 | return 1 | ||
| 48 | if v1 != v2: | ||
| 49 | co1.kill(); co2.kill() | ||
| 50 | return cmp(v1,v2) | ||
| 51 | |||
| 52 | print fcmp(range(7), x) # 0; fringes are equal | ||
| 53 | print fcmp(range(6), x) # -1; 1st list ends early | ||
| 54 | print fcmp(x, range(6)) # 1; 2nd list ends early | ||
| 55 | print fcmp(range(8), x) # 1; 2nd list ends early | ||
| 56 | print fcmp(x, range(8)) # -1; 1st list ends early | ||
| 57 | print fcmp([1,[[2],8]], | ||
| 58 | [[[1],2],8]) # 0 | ||
| 59 | print fcmp([1,[[3],8]], | ||
| 60 | [[[1],2],8]) # 1 | ||
| 61 | print fcmp([1,[[2],8]], | ||
| 62 | [[[1],2],9]) # -1 | ||
| 63 | |||
| 64 | # end of example | ||
| @@ -0,0 +1,155 @@ | |||
| 1 | # A parallelized "find(1)" using the thread module. | ||
| 2 | |||
| 3 | # This demonstrates the use of a work queue and worker threads. | ||
| 4 | # It really does do more stats/sec when using multiple threads, | ||
| 5 | # although the improvement is only about 20-30 percent. | ||
| 6 | # (That was 8 years ago. In 2002, on Linux, I can't measure | ||
| 7 | # a speedup. :-( ) | ||
| 8 | |||
| 9 | # I'm too lazy to write a command line parser for the full find(1) | ||
| 10 | # command line syntax, so the predicate it searches for is wired-in, | ||
| 11 | # see function selector() below. (It currently searches for files with | ||
| 12 | # world write permission.) | ||
| 13 | |||
| 14 | # Usage: parfind.py [-w nworkers] [directory] ... | ||
| 15 | # Default nworkers is 4 | ||
| 16 | |||
| 17 | |||
| 18 | import sys | ||
| 19 | import getopt | ||
| 20 | import string | ||
| 21 | import time | ||
| 22 | import os | ||
| 23 | from stat import * | ||
| 24 | import thread | ||
| 25 | |||
| 26 | |||
| 27 | # Work queue class. Usage: | ||
| 28 | # wq = WorkQ() | ||
| 29 | # wq.addwork(func, (arg1, arg2, ...)) # one or more calls | ||
| 30 | # wq.run(nworkers) | ||
| 31 | # The work is done when wq.run() completes. | ||
| 32 | # The function calls executed by the workers may add more work. | ||
| 33 | # Don't use keyboard interrupts! | ||
| 34 | |||
| 35 | class WorkQ: | ||
| 36 | |||
| 37 | # Invariants: | ||
| 38 | |||
| 39 | # - busy and work are only modified when mutex is locked | ||
| 40 | # - len(work) is the number of jobs ready to be taken | ||
| 41 | # - busy is the number of jobs being done | ||
| 42 | # - todo is locked iff there is no work and somebody is busy | ||
| 43 | |||
| 44 | def __init__(self): | ||
| 45 | self.mutex = thread.allocate() | ||
| 46 | self.todo = thread.allocate() | ||
| 47 | self.todo.acquire() | ||
| 48 | self.work = [] | ||
| 49 | self.busy = 0 | ||
| 50 | |||
| 51 | def addwork(self, func, args): | ||
| 52 | job = (func, args) | ||
| 53 | self.mutex.acquire() | ||
| 54 | self.work.append(job) | ||
| 55 | self.mutex.release() | ||
| 56 | if len(self.work) == 1: | ||
| 57 | self.todo.release() | ||
| 58 | |||
| 59 | def _getwork(self): | ||
| 60 | self.todo.acquire() | ||
| 61 | self.mutex.acquire() | ||
| 62 | if self.busy == 0 and len(self.work) == 0: | ||
| 63 | self.mutex.release() | ||
| 64 | self.todo.release() | ||
| 65 | return None | ||
| 66 | job = self.work[0] | ||
| 67 | del self.work[0] | ||
| 68 | self.busy = self.busy + 1 | ||
| 69 | self.mutex.release() | ||
| 70 | if len(self.work) > 0: | ||
| 71 | self.todo.release() | ||
| 72 | return job | ||
| 73 | |||
| 74 | def _donework(self): | ||
| 75 | self.mutex.acquire() | ||
| 76 | self.busy = self.busy - 1 | ||
| 77 | if self.busy == 0 and len(self.work) == 0: | ||
| 78 | self.todo.release() | ||
| 79 | self.mutex.release() | ||
| 80 | |||
| 81 | def _worker(self): | ||
| 82 | time.sleep(0.00001) # Let other threads run | ||
| 83 | while 1: | ||
| 84 | job = self._getwork() | ||
| 85 | if not job: | ||
| 86 | break | ||
| 87 | func, args = job | ||
| 88 | apply(func, args) | ||
| 89 | self._donework() | ||
| 90 | |||
| 91 | def run(self, nworkers): | ||
| 92 | if not self.work: | ||
| 93 | return # Nothing to do | ||
| 94 | for i in range(nworkers-1): | ||
| 95 | thread.start_new(self._worker, ()) | ||
| 96 | self._worker() | ||
| 97 | self.todo.acquire() | ||
| 98 | |||
| 99 | |||
| 100 | # Main program | ||
| 101 | |||
| 102 | def main(): | ||
| 103 | nworkers = 4 | ||
| 104 | opts, args = getopt.getopt(sys.argv[1:], '-w:') | ||
| 105 | for opt, arg in opts: | ||
| 106 | if opt == '-w': | ||
| 107 | nworkers = string.atoi(arg) | ||
| 108 | if not args: | ||
| 109 | args = [os.curdir] | ||
| 110 | |||
| 111 | wq = WorkQ() | ||
| 112 | for dir in args: | ||
| 113 | wq.addwork(find, (dir, selector, wq)) | ||
| 114 | |||
| 115 | t1 = time.time() | ||
| 116 | wq.run(nworkers) | ||
| 117 | t2 = time.time() | ||
| 118 | |||
| 119 | sys.stderr.write('Total time %r sec.\n' % (t2-t1)) | ||
| 120 | |||
| 121 | |||
| 122 | # The predicate -- defines what files we look for. | ||
| 123 | # Feel free to change this to suit your purpose | ||
| 124 | |||
| 125 | def selector(dir, name, fullname, stat): | ||
| 126 | # Look for world writable files that are not symlinks | ||
| 127 | return (stat[ST_MODE] & 0002) != 0 and not S_ISLNK(stat[ST_MODE]) | ||
| 128 | |||
| 129 | |||
| 130 | # The find procedure -- calls wq.addwork() for subdirectories | ||
| 131 | |||
| 132 | def find(dir, pred, wq): | ||
| 133 | try: | ||
| 134 | names = os.listdir(dir) | ||
| 135 | except os.error, msg: | ||
| 136 | print repr(dir), ':', msg | ||
| 137 | return | ||
| 138 | for name in names: | ||
| 139 | if name not in (os.curdir, os.pardir): | ||
| 140 | fullname = os.path.join(dir, name) | ||
| 141 | try: | ||
| 142 | stat = os.lstat(fullname) | ||
| 143 | except os.error, msg: | ||
| 144 | print repr(fullname), ':', msg | ||
| 145 | continue | ||
| 146 | if pred(dir, name, fullname, stat): | ||
| 147 | print fullname | ||
| 148 | if S_ISDIR(stat[ST_MODE]): | ||
| 149 | if not os.path.ismount(fullname): | ||
| 150 | wq.addwork(find, (fullname, pred, wq)) | ||
| 151 | |||
| 152 | |||
| 153 | # Call the main program | ||
| 154 | |||
| 155 | main() | ||
| @@ -0,0 +1,105 @@ | |||
| 1 | # Coroutine example: general coroutine transfers | ||
| 2 | # | ||
| 3 | # The program is a variation of a Simula 67 program due to Dahl & Hoare, | ||
| 4 | # (Dahl/Dijkstra/Hoare, Structured Programming; Academic Press, 1972) | ||
| 5 | # who in turn credit the original example to Conway. | ||
| 6 | # | ||
| 7 | # We have a number of input lines, terminated by a 0 byte. The problem | ||
| 8 | # is to squash them together into output lines containing 72 characters | ||
| 9 | # each. A semicolon must be added between input lines. Runs of blanks | ||
| 10 | # and tabs in input lines must be squashed into single blanks. | ||
| 11 | # Occurrences of "**" in input lines must be replaced by "^". | ||
| 12 | # | ||
| 13 | # Here's a test case: | ||
| 14 | |||
| 15 | test = """\ | ||
| 16 | d = sqrt(b**2 - 4*a*c) | ||
| 17 | twoa = 2*a | ||
| 18 | L = -b/twoa | ||
| 19 | R = d/twoa | ||
| 20 | A1 = L + R | ||
| 21 | A2 = L - R\0 | ||
| 22 | """ | ||
| 23 | |||
| 24 | # The program should print: | ||
| 25 | |||
| 26 | # d = sqrt(b^2 - 4*a*c);twoa = 2*a; L = -b/twoa; R = d/twoa; A1 = L + R; | ||
| 27 | #A2 = L - R | ||
| 28 | #done | ||
| 29 | |||
| 30 | # getline: delivers the next input line to its invoker | ||
| 31 | # disassembler: grabs input lines from getline, and delivers them one | ||
| 32 | # character at a time to squasher, also inserting a semicolon into | ||
| 33 | # the stream between lines | ||
| 34 | # squasher: grabs characters from disassembler and passes them on to | ||
| 35 | # assembler, first replacing "**" with "^" and squashing runs of | ||
| 36 | # whitespace | ||
| 37 | # assembler: grabs characters from squasher and packs them into lines | ||
| 38 | # with 72 character each, delivering each such line to putline; | ||
| 39 | # when it sees a null byte, passes the last line to putline and | ||
| 40 | # then kills all the coroutines | ||
| 41 | # putline: grabs lines from assembler, and just prints them | ||
| 42 | |||
| 43 | from Coroutine import * | ||
| 44 | |||
| 45 | def getline(text): | ||
| 46 | for line in string.splitfields(text, '\n'): | ||
| 47 | co.tran(codisassembler, line) | ||
| 48 | |||
| 49 | def disassembler(): | ||
| 50 | while 1: | ||
| 51 | card = co.tran(cogetline) | ||
| 52 | for i in range(len(card)): | ||
| 53 | co.tran(cosquasher, card[i]) | ||
| 54 | co.tran(cosquasher, ';') | ||
| 55 | |||
| 56 | def squasher(): | ||
| 57 | while 1: | ||
| 58 | ch = co.tran(codisassembler) | ||
| 59 | if ch == '*': | ||
| 60 | ch2 = co.tran(codisassembler) | ||
| 61 | if ch2 == '*': | ||
| 62 | ch = '^' | ||
| 63 | else: | ||
| 64 | co.tran(coassembler, ch) | ||
| 65 | ch = ch2 | ||
| 66 | if ch in ' \t': | ||
| 67 | while 1: | ||
| 68 | ch2 = co.tran(codisassembler) | ||
| 69 | if ch2 not in ' \t': | ||
| 70 | break | ||
| 71 | co.tran(coassembler, ' ') | ||
| 72 | ch = ch2 | ||
| 73 | co.tran(coassembler, ch) | ||
| 74 | |||
| 75 | def assembler(): | ||
| 76 | line = '' | ||
| 77 | while 1: | ||
| 78 | ch = co.tran(cosquasher) | ||
| 79 | if ch == '\0': | ||
| 80 | break | ||
| 81 | if len(line) == 72: | ||
| 82 | co.tran(coputline, line) | ||
| 83 | line = '' | ||
| 84 | line = line + ch | ||
| 85 | line = line + ' ' * (72 - len(line)) | ||
| 86 | co.tran(coputline, line) | ||
| 87 | co.kill() | ||
| 88 | |||
| 89 | def putline(): | ||
| 90 | while 1: | ||
| 91 | line = co.tran(coassembler) | ||
| 92 | print line | ||
| 93 | |||
| 94 | import string | ||
| 95 | co = Coroutine() | ||
| 96 | cogetline = co.create(getline, test) | ||
| 97 | coputline = co.create(putline) | ||
| 98 | coassembler = co.create(assembler) | ||
| 99 | codisassembler = co.create(disassembler) | ||
| 100 | cosquasher = co.create(squasher) | ||
| 101 | |||
| 102 | co.tran(coputline) | ||
| 103 | print 'done' | ||
| 104 | |||
| 105 | # end of example | ||
| @@ -0,0 +1,603 @@ | |||
| 1 | # Defines classes that provide synchronization objects. Note that use of | ||
| 2 | # this module requires that your Python support threads. | ||
| 3 | # | ||
| 4 | # condition(lock=None) # a POSIX-like condition-variable object | ||
| 5 | # barrier(n) # an n-thread barrier | ||
| 6 | # event() # an event object | ||
| 7 | # semaphore(n=1) # a semaphore object, with initial count n | ||
| 8 | # mrsw() # a multiple-reader single-writer lock | ||
| 9 | # | ||
| 10 | # CONDITIONS | ||
| 11 | # | ||
| 12 | # A condition object is created via | ||
| 13 | # import this_module | ||
| 14 | # your_condition_object = this_module.condition(lock=None) | ||
| 15 | # | ||
| 16 | # As explained below, a condition object has a lock associated with it, | ||
| 17 | # used in the protocol to protect condition data. You can specify a | ||
| 18 | # lock to use in the constructor, else the constructor will allocate | ||
| 19 | # an anonymous lock for you. Specifying a lock explicitly can be useful | ||
| 20 | # when more than one condition keys off the same set of shared data. | ||
| 21 | # | ||
| 22 | # Methods: | ||
| 23 | # .acquire() | ||
| 24 | # acquire the lock associated with the condition | ||
| 25 | # .release() | ||
| 26 | # release the lock associated with the condition | ||
| 27 | # .wait() | ||
| 28 | # block the thread until such time as some other thread does a | ||
| 29 | # .signal or .broadcast on the same condition, and release the | ||
| 30 | # lock associated with the condition. The lock associated with | ||
| 31 | # the condition MUST be in the acquired state at the time | ||
| 32 | # .wait is invoked. | ||
| 33 | # .signal() | ||
| 34 | # wake up exactly one thread (if any) that previously did a .wait | ||
| 35 | # on the condition; that thread will awaken with the lock associated | ||
| 36 | # with the condition in the acquired state. If no threads are | ||
| 37 | # .wait'ing, this is a nop. If more than one thread is .wait'ing on | ||
| 38 | # the condition, any of them may be awakened. | ||
| 39 | # .broadcast() | ||
| 40 | # wake up all threads (if any) that are .wait'ing on the condition; | ||
| 41 | # the threads are woken up serially, each with the lock in the | ||
| 42 | # acquired state, so should .release() as soon as possible. If no | ||
| 43 | # threads are .wait'ing, this is a nop. | ||
| 44 | # | ||
| 45 | # Note that if a thread does a .wait *while* a signal/broadcast is | ||
| 46 | # in progress, it's guaranteeed to block until a subsequent | ||
| 47 | # signal/broadcast. | ||
| 48 | # | ||
| 49 | # Secret feature: `broadcast' actually takes an integer argument, | ||
| 50 | # and will wake up exactly that many waiting threads (or the total | ||
| 51 | # number waiting, if that's less). Use of this is dubious, though, | ||
| 52 | # and probably won't be supported if this form of condition is | ||
| 53 | # reimplemented in C. | ||
| 54 | # | ||
| 55 | # DIFFERENCES FROM POSIX | ||
| 56 | # | ||
| 57 | # + A separate mutex is not needed to guard condition data. Instead, a | ||
| 58 | # condition object can (must) be .acquire'ed and .release'ed directly. | ||
| 59 | # This eliminates a common error in using POSIX conditions. | ||
| 60 | # | ||
| 61 | # + Because of implementation difficulties, a POSIX `signal' wakes up | ||
| 62 | # _at least_ one .wait'ing thread. Race conditions make it difficult | ||
| 63 | # to stop that. This implementation guarantees to wake up only one, | ||
| 64 | # but you probably shouldn't rely on that. | ||
| 65 | # | ||
| 66 | # PROTOCOL | ||
| 67 | # | ||
| 68 | # Condition objects are used to block threads until "some condition" is | ||
| 69 | # true. E.g., a thread may wish to wait until a producer pumps out data | ||
| 70 | # for it to consume, or a server may wish to wait until someone requests | ||
| 71 | # its services, or perhaps a whole bunch of threads want to wait until a | ||
| 72 | # preceding pass over the data is complete. Early models for conditions | ||
| 73 | # relied on some other thread figuring out when a blocked thread's | ||
| 74 | # condition was true, and made the other thread responsible both for | ||
| 75 | # waking up the blocked thread and guaranteeing that it woke up with all | ||
| 76 | # data in a correct state. This proved to be very delicate in practice, | ||
| 77 | # and gave conditions a bad name in some circles. | ||
| 78 | # | ||
| 79 | # The POSIX model addresses these problems by making a thread responsible | ||
| 80 | # for ensuring that its own state is correct when it wakes, and relies | ||
| 81 | # on a rigid protocol to make this easy; so long as you stick to the | ||
| 82 | # protocol, POSIX conditions are easy to "get right": | ||
| 83 | # | ||
| 84 | # A) The thread that's waiting for some arbitrarily-complex condition | ||
| 85 | # (ACC) to become true does: | ||
| 86 | # | ||
| 87 | # condition.acquire() | ||
| 88 | # while not (code to evaluate the ACC): | ||
| 89 | # condition.wait() | ||
| 90 | # # That blocks the thread, *and* releases the lock. When a | ||
| 91 | # # condition.signal() happens, it will wake up some thread that | ||
| 92 | # # did a .wait, *and* acquire the lock again before .wait | ||
| 93 | # # returns. | ||
| 94 | # # | ||
| 95 | # # Because the lock is acquired at this point, the state used | ||
| 96 | # # in evaluating the ACC is frozen, so it's safe to go back & | ||
| 97 | # # reevaluate the ACC. | ||
| 98 | # | ||
| 99 | # # At this point, ACC is true, and the thread has the condition | ||
| 100 | # # locked. | ||
| 101 | # # So code here can safely muck with the shared state that | ||
| 102 | # # went into evaluating the ACC -- if it wants to. | ||
| 103 | # # When done mucking with the shared state, do | ||
| 104 | # condition.release() | ||
| 105 | # | ||
| 106 | # B) Threads that are mucking with shared state that may affect the | ||
| 107 | # ACC do: | ||
| 108 | # | ||
| 109 | # condition.acquire() | ||
| 110 | # # muck with shared state | ||
| 111 | # condition.release() | ||
| 112 | # if it's possible that ACC is true now: | ||
| 113 | # condition.signal() # or .broadcast() | ||
| 114 | # | ||
| 115 | # Note: You may prefer to put the "if" clause before the release(). | ||
| 116 | # That's fine, but do note that anyone waiting on the signal will | ||
| 117 | # stay blocked until the release() is done (since acquiring the | ||
| 118 | # condition is part of what .wait() does before it returns). | ||
| 119 | # | ||
| 120 | # TRICK OF THE TRADE | ||
| 121 | # | ||
| 122 | # With simpler forms of conditions, it can be impossible to know when | ||
| 123 | # a thread that's supposed to do a .wait has actually done it. But | ||
| 124 | # because this form of condition releases a lock as _part_ of doing a | ||
| 125 | # wait, the state of that lock can be used to guarantee it. | ||
| 126 | # | ||
| 127 | # E.g., suppose thread A spawns thread B and later wants to wait for B to | ||
| 128 | # complete: | ||
| 129 | # | ||
| 130 | # In A: In B: | ||
| 131 | # | ||
| 132 | # B_done = condition() ... do work ... | ||
| 133 | # B_done.acquire() B_done.acquire(); B_done.release() | ||
| 134 | # spawn B B_done.signal() | ||
| 135 | # ... some time later ... ... and B exits ... | ||
| 136 | # B_done.wait() | ||
| 137 | # | ||
| 138 | # Because B_done was in the acquire'd state at the time B was spawned, | ||
| 139 | # B's attempt to acquire B_done can't succeed until A has done its | ||
| 140 | # B_done.wait() (which releases B_done). So B's B_done.signal() is | ||
| 141 | # guaranteed to be seen by the .wait(). Without the lock trick, B | ||
| 142 | # may signal before A .waits, and then A would wait forever. | ||
| 143 | # | ||
| 144 | # BARRIERS | ||
| 145 | # | ||
| 146 | # A barrier object is created via | ||
| 147 | # import this_module | ||
| 148 | # your_barrier = this_module.barrier(num_threads) | ||
| 149 | # | ||
| 150 | # Methods: | ||
| 151 | # .enter() | ||
| 152 | # the thread blocks until num_threads threads in all have done | ||
| 153 | # .enter(). Then the num_threads threads that .enter'ed resume, | ||
| 154 | # and the barrier resets to capture the next num_threads threads | ||
| 155 | # that .enter it. | ||
| 156 | # | ||
| 157 | # EVENTS | ||
| 158 | # | ||
| 159 | # An event object is created via | ||
| 160 | # import this_module | ||
| 161 | # your_event = this_module.event() | ||
| 162 | # | ||
| 163 | # An event has two states, `posted' and `cleared'. An event is | ||
| 164 | # created in the cleared state. | ||
| 165 | # | ||
| 166 | # Methods: | ||
| 167 | # | ||
| 168 | # .post() | ||
| 169 | # Put the event in the posted state, and resume all threads | ||
| 170 | # .wait'ing on the event (if any). | ||
| 171 | # | ||
| 172 | # .clear() | ||
| 173 | # Put the event in the cleared state. | ||
| 174 | # | ||
| 175 | # .is_posted() | ||
| 176 | # Returns 0 if the event is in the cleared state, or 1 if the event | ||
| 177 | # is in the posted state. | ||
| 178 | # | ||
| 179 | # .wait() | ||
| 180 | # If the event is in the posted state, returns immediately. | ||
| 181 | # If the event is in the cleared state, blocks the calling thread | ||
| 182 | # until the event is .post'ed by another thread. | ||
| 183 | # | ||
| 184 | # Note that an event, once posted, remains posted until explicitly | ||
| 185 | # cleared. Relative to conditions, this is both the strength & weakness | ||
| 186 | # of events. It's a strength because the .post'ing thread doesn't have to | ||
| 187 | # worry about whether the threads it's trying to communicate with have | ||
| 188 | # already done a .wait (a condition .signal is seen only by threads that | ||
| 189 | # do a .wait _prior_ to the .signal; a .signal does not persist). But | ||
| 190 | # it's a weakness because .clear'ing an event is error-prone: it's easy | ||
| 191 | # to mistakenly .clear an event before all the threads you intended to | ||
| 192 | # see the event get around to .wait'ing on it. But so long as you don't | ||
| 193 | # need to .clear an event, events are easy to use safely. | ||
| 194 | # | ||
| 195 | # SEMAPHORES | ||
| 196 | # | ||
| 197 | # A semaphore object is created via | ||
| 198 | # import this_module | ||
| 199 | # your_semaphore = this_module.semaphore(count=1) | ||
| 200 | # | ||
| 201 | # A semaphore has an integer count associated with it. The initial value | ||
| 202 | # of the count is specified by the optional argument (which defaults to | ||
| 203 | # 1) passed to the semaphore constructor. | ||
| 204 | # | ||
| 205 | # Methods: | ||
| 206 | # | ||
| 207 | # .p() | ||
| 208 | # If the semaphore's count is greater than 0, decrements the count | ||
| 209 | # by 1 and returns. | ||
| 210 | # Else if the semaphore's count is 0, blocks the calling thread | ||
| 211 | # until a subsequent .v() increases the count. When that happens, | ||
| 212 | # the count will be decremented by 1 and the calling thread resumed. | ||
| 213 | # | ||
| 214 | # .v() | ||
| 215 | # Increments the semaphore's count by 1, and wakes up a thread (if | ||
| 216 | # any) blocked by a .p(). It's an (detected) error for a .v() to | ||
| 217 | # increase the semaphore's count to a value larger than the initial | ||
| 218 | # count. | ||
| 219 | # | ||
| 220 | # MULTIPLE-READER SINGLE-WRITER LOCKS | ||
| 221 | # | ||
| 222 | # A mrsw lock is created via | ||
| 223 | # import this_module | ||
| 224 | # your_mrsw_lock = this_module.mrsw() | ||
| 225 | # | ||
| 226 | # This kind of lock is often useful with complex shared data structures. | ||
| 227 | # The object lets any number of "readers" proceed, so long as no thread | ||
| 228 | # wishes to "write". When a (one or more) thread declares its intention | ||
| 229 | # to "write" (e.g., to update a shared structure), all current readers | ||
| 230 | # are allowed to finish, and then a writer gets exclusive access; all | ||
| 231 | # other readers & writers are blocked until the current writer completes. | ||
| 232 | # Finally, if some thread is waiting to write and another is waiting to | ||
| 233 | # read, the writer takes precedence. | ||
| 234 | # | ||
| 235 | # Methods: | ||
| 236 | # | ||
| 237 | # .read_in() | ||
| 238 | # If no thread is writing or waiting to write, returns immediately. | ||
| 239 | # Else blocks until no thread is writing or waiting to write. So | ||
| 240 | # long as some thread has completed a .read_in but not a .read_out, | ||
| 241 | # writers are blocked. | ||
| 242 | # | ||
| 243 | # .read_out() | ||
| 244 | # Use sometime after a .read_in to declare that the thread is done | ||
| 245 | # reading. When all threads complete reading, a writer can proceed. | ||
| 246 | # | ||
| 247 | # .write_in() | ||
| 248 | # If no thread is writing (has completed a .write_in, but hasn't yet | ||
| 249 | # done a .write_out) or reading (similarly), returns immediately. | ||
| 250 | # Else blocks the calling thread, and threads waiting to read, until | ||
| 251 | # the current writer completes writing or all the current readers | ||
| 252 | # complete reading; if then more than one thread is waiting to | ||
| 253 | # write, one of them is allowed to proceed, but which one is not | ||
| 254 | # specified. | ||
| 255 | # | ||
| 256 | # .write_out() | ||
| 257 | # Use sometime after a .write_in to declare that the thread is done | ||
| 258 | # writing. Then if some other thread is waiting to write, it's | ||
| 259 | # allowed to proceed. Else all threads (if any) waiting to read are | ||
| 260 | # allowed to proceed. | ||
| 261 | # | ||
| 262 | # .write_to_read() | ||
| 263 | # Use instead of a .write_in to declare that the thread is done | ||
| 264 | # writing but wants to continue reading without other writers | ||
| 265 | # intervening. If there are other threads waiting to write, they | ||
| 266 | # are allowed to proceed only if the current thread calls | ||
| 267 | # .read_out; threads waiting to read are only allowed to proceed | ||
| 268 | # if there are are no threads waiting to write. (This is a | ||
| 269 | # weakness of the interface!) | ||
| 270 | |||
| 271 | import thread | ||
| 272 | |||
| 273 | class condition: | ||
| 274 | def __init__(self, lock=None): | ||
| 275 | # the lock actually used by .acquire() and .release() | ||
| 276 | if lock is None: | ||
| 277 | self.mutex = thread.allocate_lock() | ||
| 278 | else: | ||
| 279 | if hasattr(lock, 'acquire') and \ | ||
| 280 | hasattr(lock, 'release'): | ||
| 281 | self.mutex = lock | ||
| 282 | else: | ||
| 283 | raise TypeError, 'condition constructor requires ' \ | ||
| 284 | 'a lock argument' | ||
| 285 | |||
| 286 | # lock used to block threads until a signal | ||
| 287 | self.checkout = thread.allocate_lock() | ||
| 288 | self.checkout.acquire() | ||
| 289 | |||
| 290 | # internal critical-section lock, & the data it protects | ||
| 291 | self.idlock = thread.allocate_lock() | ||
| 292 | self.id = 0 | ||
| 293 | self.waiting = 0 # num waiters subject to current release | ||
| 294 | self.pending = 0 # num waiters awaiting next signal | ||
| 295 | self.torelease = 0 # num waiters to release | ||
| 296 | self.releasing = 0 # 1 iff release is in progress | ||
| 297 | |||
| 298 | def acquire(self): | ||
| 299 | self.mutex.acquire() | ||
| 300 | |||
| 301 | def release(self): | ||
| 302 | self.mutex.release() | ||
| 303 | |||
| 304 | def wait(self): | ||
| 305 | mutex, checkout, idlock = self.mutex, self.checkout, self.idlock | ||
| 306 | if not mutex.locked(): | ||
| 307 | raise ValueError, \ | ||
| 308 | "condition must be .acquire'd when .wait() invoked" | ||
| 309 | |||
| 310 | idlock.acquire() | ||
| 311 | myid = self.id | ||
| 312 | self.pending = self.pending + 1 | ||
| 313 | idlock.release() | ||
| 314 | |||
| 315 | mutex.release() | ||
| 316 | |||
| 317 | while 1: | ||
| 318 | checkout.acquire(); idlock.acquire() | ||
| 319 | if myid < self.id: | ||
| 320 | break | ||
| 321 | checkout.release(); idlock.release() | ||
| 322 | |||
| 323 | self.waiting = self.waiting - 1 | ||
| 324 | self.torelease = self.torelease - 1 | ||
| 325 | if self.torelease: | ||
| 326 | checkout.release() | ||
| 327 | else: | ||
| 328 | self.releasing = 0 | ||
| 329 | if self.waiting == self.pending == 0: | ||
| 330 | self.id = 0 | ||
| 331 | idlock.release() | ||
| 332 | mutex.acquire() | ||
| 333 | |||
| 334 | def signal(self): | ||
| 335 | self.broadcast(1) | ||
| 336 | |||
| 337 | def broadcast(self, num = -1): | ||
| 338 | if num < -1: | ||
| 339 | raise ValueError, '.broadcast called with num %r' % (num,) | ||
| 340 | if num == 0: | ||
| 341 | return | ||
| 342 | self.idlock.acquire() | ||
| 343 | if self.pending: | ||
| 344 | self.waiting = self.waiting + self.pending | ||
| 345 | self.pending = 0 | ||
| 346 | self.id = self.id + 1 | ||
| 347 | if num == -1: | ||
| 348 | self.torelease = self.waiting | ||
| 349 | else: | ||
| 350 | self.torelease = min( self.waiting, | ||
| 351 | self.torelease + num ) | ||
| 352 | if self.torelease and not self.releasing: | ||
| 353 | self.releasing = 1 | ||
| 354 | self.checkout.release() | ||
| 355 | self.idlock.release() | ||
| 356 | |||
| 357 | class barrier: | ||
| 358 | def __init__(self, n): | ||
| 359 | self.n = n | ||
| 360 | self.togo = n | ||
| 361 | self.full = condition() | ||
| 362 | |||
| 363 | def enter(self): | ||
| 364 | full = self.full | ||
| 365 | full.acquire() | ||
| 366 | self.togo = self.togo - 1 | ||
| 367 | if self.togo: | ||
| 368 | full.wait() | ||
| 369 | else: | ||
| 370 | self.togo = self.n | ||
| 371 | full.broadcast() | ||
| 372 | full.release() | ||
| 373 | |||
| 374 | class event: | ||
| 375 | def __init__(self): | ||
| 376 | self.state = 0 | ||
| 377 | self.posted = condition() | ||
| 378 | |||
| 379 | def post(self): | ||
| 380 | self.posted.acquire() | ||
| 381 | self.state = 1 | ||
| 382 | self.posted.broadcast() | ||
| 383 | self.posted.release() | ||
| 384 | |||
| 385 | def clear(self): | ||
| 386 | self.posted.acquire() | ||
| 387 | self.state = 0 | ||
| 388 | self.posted.release() | ||
| 389 | |||
| 390 | def is_posted(self): | ||
| 391 | self.posted.acquire() | ||
| 392 | answer = self.state | ||
| 393 | self.posted.release() | ||
| 394 | return answer | ||
| 395 | |||
| 396 | def wait(self): | ||
| 397 | self.posted.acquire() | ||
| 398 | if not self.state: | ||
| 399 | self.posted.wait() | ||
| 400 | self.posted.release() | ||
| 401 | |||
| 402 | class semaphore: | ||
| 403 | def __init__(self, count=1): | ||
| 404 | if count <= 0: | ||
| 405 | raise ValueError, 'semaphore count %d; must be >= 1' % count | ||
| 406 | self.count = count | ||
| 407 | self.maxcount = count | ||
| 408 | self.nonzero = condition() | ||
| 409 | |||
| 410 | def p(self): | ||
| 411 | self.nonzero.acquire() | ||
| 412 | while self.count == 0: | ||
| 413 | self.nonzero.wait() | ||
| 414 | self.count = self.count - 1 | ||
| 415 | self.nonzero.release() | ||
| 416 | |||
| 417 | def v(self): | ||
| 418 | self.nonzero.acquire() | ||
| 419 | if self.count == self.maxcount: | ||
| 420 | raise ValueError, '.v() tried to raise semaphore count above ' \ | ||
| 421 | 'initial value %r' % self.maxcount | ||
| 422 | self.count = self.count + 1 | ||
| 423 | self.nonzero.signal() | ||
| 424 | self.nonzero.release() | ||
| 425 | |||
| 426 | class mrsw: | ||
| 427 | def __init__(self): | ||
| 428 | # critical-section lock & the data it protects | ||
| 429 | self.rwOK = thread.allocate_lock() | ||
| 430 | self.nr = 0 # number readers actively reading (not just waiting) | ||
| 431 | self.nw = 0 # number writers either waiting to write or writing | ||
| 432 | self.writing = 0 # 1 iff some thread is writing | ||
| 433 | |||
| 434 | # conditions | ||
| 435 | self.readOK = condition(self.rwOK) # OK to unblock readers | ||
| 436 | self.writeOK = condition(self.rwOK) # OK to unblock writers | ||
| 437 | |||
| 438 | def read_in(self): | ||
| 439 | self.rwOK.acquire() | ||
| 440 | while self.nw: | ||
| 441 | self.readOK.wait() | ||
| 442 | self.nr = self.nr + 1 | ||
| 443 | self.rwOK.release() | ||
| 444 | |||
| 445 | def read_out(self): | ||
| 446 | self.rwOK.acquire() | ||
| 447 | if self.nr <= 0: | ||
| 448 | raise ValueError, \ | ||
| 449 | '.read_out() invoked without an active reader' | ||
| 450 | self.nr = self.nr - 1 | ||
| 451 | if self.nr == 0: | ||
| 452 | self.writeOK.signal() | ||
| 453 | self.rwOK.release() | ||
| 454 | |||
| 455 | def write_in(self): | ||
| 456 | self.rwOK.acquire() | ||
| 457 | self.nw = self.nw + 1 | ||
| 458 | while self.writing or self.nr: | ||
| 459 | self.writeOK.wait() | ||
| 460 | self.writing = 1 | ||
| 461 | self.rwOK.release() | ||
| 462 | |||
| 463 | def write_out(self): | ||
| 464 | self.rwOK.acquire() | ||
| 465 | if not self.writing: | ||
| 466 | raise ValueError, \ | ||
| 467 | '.write_out() invoked without an active writer' | ||
| 468 | self.writing = 0 | ||
| 469 | self.nw = self.nw - 1 | ||
| 470 | if self.nw: | ||
| 471 | self.writeOK.signal() | ||
| 472 | else: | ||
| 473 | self.readOK.broadcast() | ||
| 474 | self.rwOK.release() | ||
| 475 | |||
| 476 | def write_to_read(self): | ||
| 477 | self.rwOK.acquire() | ||
| 478 | if not self.writing: | ||
| 479 | raise ValueError, \ | ||
| 480 | '.write_to_read() invoked without an active writer' | ||
| 481 | self.writing = 0 | ||
| 482 | self.nw = self.nw - 1 | ||
| 483 | self.nr = self.nr + 1 | ||
| 484 | if not self.nw: | ||
| 485 | self.readOK.broadcast() | ||
| 486 | self.rwOK.release() | ||
| 487 | |||
| 488 | # The rest of the file is a test case, that runs a number of parallelized | ||
| 489 | # quicksorts in parallel. If it works, you'll get about 600 lines of | ||
| 490 | # tracing output, with a line like | ||
| 491 | # test passed! 209 threads created in all | ||
| 492 | # as the last line. The content and order of preceding lines will | ||
| 493 | # vary across runs. | ||
| 494 | |||
| 495 | def _new_thread(func, *args): | ||
| 496 | global TID | ||
| 497 | tid.acquire(); id = TID = TID+1; tid.release() | ||
| 498 | io.acquire(); alive.append(id); \ | ||
| 499 | print 'starting thread', id, '--', len(alive), 'alive'; \ | ||
| 500 | io.release() | ||
| 501 | thread.start_new_thread( func, (id,) + args ) | ||
| 502 | |||
| 503 | def _qsort(tid, a, l, r, finished): | ||
| 504 | # sort a[l:r]; post finished when done | ||
| 505 | io.acquire(); print 'thread', tid, 'qsort', l, r; io.release() | ||
| 506 | if r-l > 1: | ||
| 507 | pivot = a[l] | ||
| 508 | j = l+1 # make a[l:j] <= pivot, and a[j:r] > pivot | ||
| 509 | for i in range(j, r): | ||
| 510 | if a[i] <= pivot: | ||
| 511 | a[j], a[i] = a[i], a[j] | ||
| 512 | j = j + 1 | ||
| 513 | a[l], a[j-1] = a[j-1], pivot | ||
| 514 | |||
| 515 | l_subarray_sorted = event() | ||
| 516 | r_subarray_sorted = event() | ||
| 517 | _new_thread(_qsort, a, l, j-1, l_subarray_sorted) | ||
| 518 | _new_thread(_qsort, a, j, r, r_subarray_sorted) | ||
| 519 | l_subarray_sorted.wait() | ||
| 520 | r_subarray_sorted.wait() | ||
| 521 | |||
| 522 | io.acquire(); print 'thread', tid, 'qsort done'; \ | ||
| 523 | alive.remove(tid); io.release() | ||
| 524 | finished.post() | ||
| 525 | |||
| 526 | def _randarray(tid, a, finished): | ||
| 527 | io.acquire(); print 'thread', tid, 'randomizing array'; \ | ||
| 528 | io.release() | ||
| 529 | for i in range(1, len(a)): | ||
| 530 | wh.acquire(); j = randint(0,i); wh.release() | ||
| 531 | a[i], a[j] = a[j], a[i] | ||
| 532 | io.acquire(); print 'thread', tid, 'randomizing done'; \ | ||
| 533 | alive.remove(tid); io.release() | ||
| 534 | finished.post() | ||
| 535 | |||
| 536 | def _check_sort(a): | ||
| 537 | if a != range(len(a)): | ||
| 538 | raise ValueError, ('a not sorted', a) | ||
| 539 | |||
| 540 | def _run_one_sort(tid, a, bar, done): | ||
| 541 | # randomize a, and quicksort it | ||
| 542 | # for variety, all the threads running this enter a barrier | ||
| 543 | # at the end, and post `done' after the barrier exits | ||
| 544 | io.acquire(); print 'thread', tid, 'randomizing', a; \ | ||
| 545 | io.release() | ||
| 546 | finished = event() | ||
| 547 | _new_thread(_randarray, a, finished) | ||
| 548 | finished.wait() | ||
| 549 | |||
| 550 | io.acquire(); print 'thread', tid, 'sorting', a; io.release() | ||
| 551 | finished.clear() | ||
| 552 | _new_thread(_qsort, a, 0, len(a), finished) | ||
| 553 | finished.wait() | ||
| 554 | _check_sort(a) | ||
| 555 | |||
| 556 | io.acquire(); print 'thread', tid, 'entering barrier'; \ | ||
| 557 | io.release() | ||
| 558 | bar.enter() | ||
| 559 | io.acquire(); print 'thread', tid, 'leaving barrier'; \ | ||
| 560 | io.release() | ||
| 561 | io.acquire(); alive.remove(tid); io.release() | ||
| 562 | bar.enter() # make sure they've all removed themselves from alive | ||
| 563 | ## before 'done' is posted | ||
| 564 | bar.enter() # just to be cruel | ||
| 565 | done.post() | ||
| 566 | |||
| 567 | def test(): | ||
| 568 | global TID, tid, io, wh, randint, alive | ||
| 569 | import random | ||
| 570 | randint = random.randint | ||
| 571 | |||
| 572 | TID = 0 # thread ID (1, 2, ...) | ||
| 573 | tid = thread.allocate_lock() # for changing TID | ||
| 574 | io = thread.allocate_lock() # for printing, and 'alive' | ||
| 575 | wh = thread.allocate_lock() # for calls to random | ||
| 576 | alive = [] # IDs of active threads | ||
| 577 | |||
| 578 | NSORTS = 5 | ||
| 579 | arrays = [] | ||
| 580 | for i in range(NSORTS): | ||
| 581 | arrays.append( range( (i+1)*10 ) ) | ||
| 582 | |||
| 583 | bar = barrier(NSORTS) | ||
| 584 | finished = event() | ||
| 585 | for i in range(NSORTS): | ||
| 586 | _new_thread(_run_one_sort, arrays[i], bar, finished) | ||
| 587 | finished.wait() | ||
| 588 | |||
| 589 | print 'all threads done, and checking results ...' | ||
| 590 | if alive: | ||
| 591 | raise ValueError, ('threads still alive at end', alive) | ||
| 592 | for i in range(NSORTS): | ||
| 593 | a = arrays[i] | ||
| 594 | if len(a) != (i+1)*10: | ||
| 595 | raise ValueError, ('length of array', i, 'screwed up') | ||
| 596 | _check_sort(a) | ||
| 597 | |||
| 598 | print 'test passed!', TID, 'threads created in all' | ||
| 599 | |||
| 600 | if __name__ == '__main__': | ||
| 601 | test() | ||
| 602 | |||
| 603 | # end of module | ||
| @@ -0,0 +1,114 @@ | |||
| 1 | # Minimal interface to the Internet telnet protocol. | ||
| 2 | # | ||
| 3 | # *** modified to use threads *** | ||
| 4 | # | ||
| 5 | # It refuses all telnet options and does not recognize any of the other | ||
| 6 | # telnet commands, but can still be used to connect in line-by-line mode. | ||
| 7 | # It's also useful to play with a number of other services, | ||
| 8 | # like time, finger, smtp and even ftp. | ||
| 9 | # | ||
| 10 | # Usage: telnet host [port] | ||
| 11 | # | ||
| 12 | # The port may be a service name or a decimal port number; | ||
| 13 | # it defaults to 'telnet'. | ||
| 14 | |||
| 15 | |||
| 16 | import sys, os, time | ||
| 17 | from socket import * | ||
| 18 | import thread | ||
| 19 | |||
| 20 | BUFSIZE = 8*1024 | ||
| 21 | |||
| 22 | # Telnet protocol characters | ||
| 23 | |||
| 24 | IAC = chr(255) # Interpret as command | ||
| 25 | DONT = chr(254) | ||
| 26 | DO = chr(253) | ||
| 27 | WONT = chr(252) | ||
| 28 | WILL = chr(251) | ||
| 29 | |||
| 30 | def main(): | ||
| 31 | if len(sys.argv) < 2: | ||
| 32 | sys.stderr.write('usage: telnet hostname [port]\n') | ||
| 33 | sys.exit(2) | ||
| 34 | host = sys.argv[1] | ||
| 35 | try: | ||
| 36 | hostaddr = gethostbyname(host) | ||
| 37 | except error: | ||
| 38 | sys.stderr.write(sys.argv[1] + ': bad host name\n') | ||
| 39 | sys.exit(2) | ||
| 40 | # | ||
| 41 | if len(sys.argv) > 2: | ||
| 42 | servname = sys.argv[2] | ||
| 43 | else: | ||
| 44 | servname = 'telnet' | ||
| 45 | # | ||
| 46 | if '0' <= servname[:1] <= '9': | ||
| 47 | port = eval(servname) | ||
| 48 | else: | ||
| 49 | try: | ||
| 50 | port = getservbyname(servname, 'tcp') | ||
| 51 | except error: | ||
| 52 | sys.stderr.write(servname + ': bad tcp service name\n') | ||
| 53 | sys.exit(2) | ||
| 54 | # | ||
| 55 | s = socket(AF_INET, SOCK_STREAM) | ||
| 56 | # | ||
| 57 | try: | ||
| 58 | s.connect((host, port)) | ||
| 59 | except error, msg: | ||
| 60 | sys.stderr.write('connect failed: %r\n' % (msg,)) | ||
| 61 | sys.exit(1) | ||
| 62 | # | ||
| 63 | thread.start_new(child, (s,)) | ||
| 64 | parent(s) | ||
| 65 | |||
| 66 | def parent(s): | ||
| 67 | # read socket, write stdout | ||
| 68 | iac = 0 # Interpret next char as command | ||
| 69 | opt = '' # Interpret next char as option | ||
| 70 | while 1: | ||
| 71 | data, dummy = s.recvfrom(BUFSIZE) | ||
| 72 | if not data: | ||
| 73 | # EOF -- exit | ||
| 74 | sys.stderr.write( '(Closed by remote host)\n') | ||
| 75 | sys.exit(1) | ||
| 76 | cleandata = '' | ||
| 77 | for c in data: | ||
| 78 | if opt: | ||
| 79 | print ord(c) | ||
| 80 | ## print '(replying: %r)' % (opt+c,) | ||
| 81 | s.send(opt + c) | ||
| 82 | opt = '' | ||
| 83 | elif iac: | ||
| 84 | iac = 0 | ||
| 85 | if c == IAC: | ||
| 86 | cleandata = cleandata + c | ||
| 87 | elif c in (DO, DONT): | ||
| 88 | if c == DO: print '(DO)', | ||
| 89 | else: print '(DONT)', | ||
| 90 | opt = IAC + WONT | ||
| 91 | elif c in (WILL, WONT): | ||
| 92 | if c == WILL: print '(WILL)', | ||
| 93 | else: print '(WONT)', | ||
| 94 | opt = IAC + DONT | ||
| 95 | else: | ||
| 96 | print '(command)', ord(c) | ||
| 97 | elif c == IAC: | ||
| 98 | iac = 1 | ||
| 99 | print '(IAC)', | ||
| 100 | else: | ||
| 101 | cleandata = cleandata + c | ||
| 102 | sys.stdout.write(cleandata) | ||
| 103 | sys.stdout.flush() | ||
| 104 | ## print 'Out:', repr(cleandata) | ||
| 105 | |||
| 106 | def child(s): | ||
| 107 | # read stdin, write socket | ||
| 108 | while 1: | ||
| 109 | line = sys.stdin.readline() | ||
| 110 | ## print 'Got:', repr(line) | ||
| 111 | if not line: break | ||
| 112 | s.send(line) | ||
| 113 | |||
| 114 | main() | ||
| @@ -0,0 +1,89 @@ | |||
| 1 | $Id: INSTALL.txt 24230 2001-11-11 14:07:37Z loewis $ | ||
| 2 | |||
| 3 | Installing Tix.py | ||
| 4 | ---------------- | ||
| 5 | |||
| 6 | 0) To use Tix.py, you need Tcl/Tk (V8.3.3), Tix (V8.1.1) and Python (V2.1.1). | ||
| 7 | Tix.py has been written and tested on a Intel Pentium running RH Linux 5.2 | ||
| 8 | and Mandrake Linux 7.0 and Windows with the above mentioned packages. | ||
| 9 | |||
| 10 | Older versions, e.g. Tix 4.1 and Tk 8.0, might also work. | ||
| 11 | |||
| 12 | There is nothing OS-specific in Tix.py itself so it should work on | ||
| 13 | any machine with Tix and Python installed. You can get Tcl and Tk | ||
| 14 | from http://dev.scriptics.com and Tix from http://tix.sourceforge.net. | ||
| 15 | |||
| 16 | 1) Build and install Tcl/Tk 8.3. Build and install Tix 8.1. | ||
| 17 | Ensure that Tix is properly installed by running tixwish and executing | ||
| 18 | the demo programs. Under Unix, use the --enable-shared configure option | ||
| 19 | for all three. We recommend tcl8.3.3 for this release of Tix.py. | ||
| 20 | |||
| 21 | 2a) If you have a distribution like ActiveState with a tcl subdirectory | ||
| 22 | of $PYTHONHOME, which contains the directories tcl8.3 and tk8.3, | ||
| 23 | make a directory tix8.1 as well. Recursively copy the files from | ||
| 24 | <tix>/library to $PYTHONHOME/lib/tix8.1, and copy the dynamic library | ||
| 25 | (tix8183.dll or libtix8.1.8.3.so) to the same place as the tcl dynamic | ||
| 26 | libraries ($PYTHONHOME/Dlls or lib/python-2.1/lib-dynload). In this | ||
| 27 | case you are all installed, and you can skip to the end. | ||
| 28 | |||
| 29 | 2b) Modify Modules/Setup.dist and setup.py to change the version of the | ||
| 30 | tix library from tix4.1.8.0 to tix8.1.8.3 | ||
| 31 | These modified files can be used for Tkinter with or without Tix. | ||
| 32 | |||
| 33 | 3) The default is to build dynamically, and use the Tcl 'package require'. | ||
| 34 | To build statically, modify the Modules/Setup file to link in the Tix | ||
| 35 | library according to the comments in the file. On Linux this looks like: | ||
| 36 | |||
| 37 | # *** Always uncomment this (leave the leading underscore in!): | ||
| 38 | _tkinter _tkinter.c tkappinit.c -DWITH_APPINIT \ | ||
| 39 | # *** Uncomment and edit to reflect where your Tcl/Tk libraries are: | ||
| 40 | -L/usr/local/lib \ | ||
| 41 | # *** Uncomment and edit to reflect where your Tcl/Tk headers are: | ||
| 42 | -I/usr/local/include \ | ||
| 43 | # *** Uncomment and edit to reflect where your X11 header files are: | ||
| 44 | -I/usr/X11R6/include \ | ||
| 45 | # *** Or uncomment this for Solaris: | ||
| 46 | # -I/usr/openwin/include \ | ||
| 47 | # *** Uncomment and edit for BLT extension only: | ||
| 48 | # -DWITH_BLT -I/usr/local/blt/blt8.0-unoff/include -lBLT8.0 \ | ||
| 49 | # *** Uncomment and edit for PIL (TkImaging) extension only: | ||
| 50 | # (See http://www.pythonware.com/products/pil/ for more info) | ||
| 51 | # -DWITH_PIL -I../Extensions/Imaging/libImaging tkImaging.c \ | ||
| 52 | # *** Uncomment and edit for TOGL extension only: | ||
| 53 | # -DWITH_TOGL togl.c \ | ||
| 54 | # *** Uncomment and edit for Tix extension only: | ||
| 55 | -DWITH_TIX -ltix8.1.8.3 \ | ||
| 56 | # *** Uncomment and edit to reflect your Tcl/Tk versions: | ||
| 57 | -ltk8.3 -ltcl8.3 \ | ||
| 58 | # *** Uncomment and edit to reflect where your X11 libraries are: | ||
| 59 | -L/usr/X11R6/lib \ | ||
| 60 | # *** Or uncomment this for Solaris: | ||
| 61 | # -L/usr/openwin/lib \ | ||
| 62 | # *** Uncomment these for TOGL extension only: | ||
| 63 | # -lGL -lGLU -lXext -lXmu \ | ||
| 64 | # *** Uncomment for AIX: | ||
| 65 | # -lld \ | ||
| 66 | # *** Always uncomment this; X11 libraries to link with: | ||
| 67 | -lX11 | ||
| 68 | |||
| 69 | 4) Rebuild Python and reinstall. | ||
| 70 | |||
| 71 | You should now have a working Tix implementation in Python. To see if all | ||
| 72 | is as it should be, run the 'tixwidgets.py' script in the Demo/tix directory. | ||
| 73 | Under X windows, do | ||
| 74 | /usr/local/bin/python Demo/tix/tixwidgets.py | ||
| 75 | |||
| 76 | If this does not work, you may need to tell python where to find | ||
| 77 | the Tcl, Tk and Tix library files. This is done by setting the | ||
| 78 | TCL_LIBRARY, TK_LIBRARY and TIX_LIBRARY environment variables. Try this: | ||
| 79 | |||
| 80 | env TCL_LIBRARY=/usr/local/lib/tcl8.3 \ | ||
| 81 | TK_LIBRARY=/usr/local/lib/tk8.3 \ | ||
| 82 | TIX_LIBRARY=/usr/local/lib/tix8.1 \ | ||
| 83 | /usr/local/bin/python Demo/tix/tixwidgets.py | ||
| 84 | |||
| 85 | |||
| 86 | If you find any bugs or have suggestions for improvement, please report them | ||
| 87 | via http://tix.sourceforge.net | ||
| 88 | |||
| 89 | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | About Tix.py | ||
| 2 | ----------- | ||
| 3 | |||
| 4 | Tix.py is based on an idea of Jean-Marc Lugrin (lugrin@ms.com) who wrote | ||
| 5 | pytix (another Python-Tix marriage). Tix widgets are an attractive and | ||
| 6 | useful extension to Tk. See http://tix.sourceforge.net | ||
| 7 | for more details about Tix and how to get it. | ||
| 8 | |||
| 9 | Features: | ||
| 10 | 1) It is almost complete. | ||
| 11 | 2) Tix widgets are represented by classes in Python. Sub-widgets | ||
| 12 | are members of the mega-widget class. For example, if a | ||
| 13 | particular TixWidget (e.g. ScrolledText) has an embedded widget | ||
| 14 | (Text in this case), it is possible to call the methods of the | ||
| 15 | child directly. | ||
| 16 | 3) The members of the class are created automatically. In the case | ||
| 17 | of widgets like ButtonBox, the members are added dynamically. | ||
| 18 | |||
| 19 | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | /* XPM */ | ||
| 2 | static char * about_xpm[] = { | ||
| 3 | "50 40 7 1", | ||
| 4 | " s None c None", | ||
| 5 | ". c black", | ||
| 6 | "X c white", | ||
| 7 | "o c gray70", | ||
| 8 | "O c navy", | ||
| 9 | "+ c red", | ||
| 10 | "@ c yellow", | ||
| 11 | " ", | ||
| 12 | " ", | ||
| 13 | " ", | ||
| 14 | " ................................. ", | ||
| 15 | " ..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXoo. ", | ||
| 16 | " .XooooooooooooooooooooooooooooooXo. ", | ||
| 17 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXooXo. ", | ||
| 18 | " ..oooooooooooooooooooooooooooooooXo. ", | ||
| 19 | " ...............................XoXo. ", | ||
| 20 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 21 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 22 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 23 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 24 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo.++++ ", | ||
| 25 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo+++ ", | ||
| 26 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.Xo+++++ ", | ||
| 27 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.Xo++++++ ", | ||
| 28 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.Xo+++ + ", | ||
| 29 | " .OOOOO@@@@@OOOOOOOOOOOOOOOOOOO.Xo++. ", | ||
| 30 | " .OOOOOOO@OOOOO@OOOOOOOOOOOOOOO.XoXo. ", | ||
| 31 | " .OOOOOOO@OOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 32 | " .OOOOOOO@OOOO@@OOO@OOO@OOOOOOO.XoXo. ", | ||
| 33 | " .OOOOOOO@OOOOO@OOOO@O@OOOOOOOO.XoXo. ", | ||
| 34 | " .OOOOOOO@OOOOO@OOOOO@OOOOOOOOO.XoXo. ", | ||
| 35 | " .OOOOOOO@OOOOO@OOOOO@OOOOOOOOO.XoXo. ", | ||
| 36 | " .OOOOOOO@OOOOO@OOOO@O@OOOOOOOO.XoXo. ", | ||
| 37 | " .OOOOOOO@OOOO@@@OO@OOO@OOOOOOO.XoXo. ", | ||
| 38 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 39 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 40 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 41 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 42 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 43 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XoXo. ", | ||
| 44 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.Xo.. ", | ||
| 45 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.Xo ", | ||
| 46 | " OOOOOOOOOOOOOOOOOOOOOOOOOOOOO.X. ", | ||
| 47 | " ............................. ", | ||
| 48 | " ", | ||
| 49 | " ", | ||
| 50 | " "}; | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | #define bold_width 16 | ||
| 2 | #define bold_height 16 | ||
| 3 | static unsigned char bold_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x0f, 0x18, 0x1c, 0x18, 0x18, | ||
| 5 | 0x18, 0x18, 0x18, 0x1c, 0xf8, 0x0f, 0xf8, 0x0f, 0x18, 0x18, 0x18, 0x30, | ||
| 6 | 0x18, 0x30, 0x18, 0x38, 0xfc, 0x3f, 0xfc, 0x1f}; | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | #define capital_width 16 | ||
| 2 | #define capital_height 16 | ||
| 3 | static unsigned char capital_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x08, 0x30, 0x0c, 0x30, 0x06, | ||
| 5 | 0x30, 0x03, 0xb0, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x01, 0xb0, 0x03, | ||
| 6 | 0x30, 0x07, 0x30, 0x0e, 0x30, 0x1c, 0x00, 0x00}; | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | #define centerj_width 16 | ||
| 2 | #define centerj_height 16 | ||
| 3 | static unsigned char centerj_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3e, 0x00, 0x00, 0xc0, 0x0d, | ||
| 5 | 0x00, 0x00, 0x58, 0x77, 0x00, 0x00, 0xb0, 0x3b, 0x00, 0x00, 0xdc, 0xf7, | ||
| 6 | 0x00, 0x00, 0xf0, 0x3e, 0x00, 0x00, 0xd8, 0x7e}; | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | #define combobox_width 32 | ||
| 2 | #define combobox_height 32 | ||
| 3 | static unsigned char combobox_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 5 | 0xfc, 0xff, 0xff, 0x3e, 0x04, 0x00, 0x80, 0x2a, 0x04, 0x00, 0x80, 0x2a, | ||
| 6 | 0x04, 0x00, 0x80, 0x2a, 0x04, 0x00, 0x80, 0x2b, 0xfc, 0xff, 0xff, 0x3e, | ||
| 7 | 0x08, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x3e, 0x08, 0x00, 0x00, 0x2a, | ||
| 8 | 0x28, 0x49, 0x00, 0x2a, 0x08, 0x00, 0x00, 0x3e, 0x08, 0x00, 0x00, 0x22, | ||
| 9 | 0x08, 0x00, 0x00, 0x22, 0x28, 0x49, 0x12, 0x22, 0x08, 0x00, 0x00, 0x22, | ||
| 10 | 0x08, 0x00, 0x00, 0x22, 0x08, 0x00, 0x00, 0x22, 0x28, 0x49, 0x02, 0x22, | ||
| 11 | 0x08, 0x00, 0x00, 0x3e, 0x08, 0x00, 0x00, 0x2a, 0x08, 0x00, 0x00, 0x2a, | ||
| 12 | 0xf8, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 13 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 14 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* XPM */ | ||
| 2 | static char * combobox_xpm[] = { | ||
| 3 | "50 40 6 1", | ||
| 4 | " s None c None", | ||
| 5 | ". c black", | ||
| 6 | "X c white", | ||
| 7 | "o c #FFFF80808080", | ||
| 8 | "O c gray70", | ||
| 9 | "+ c #808000008080", | ||
| 10 | " ", | ||
| 11 | " ", | ||
| 12 | " ", | ||
| 13 | " .................................... XXXXXXX ", | ||
| 14 | " .ooooooooooooooooooooooooooooooooooX X . . ", | ||
| 15 | " .ooooooooooooooooooooooooooooooooooX X . . ", | ||
| 16 | " .oooo.oooooooooooooooooooooooooooooX X . . ", | ||
| 17 | " .oo.o..oo.o.oo.o.ooooooooooooooooooX X . . ", | ||
| 18 | " .o..o.o.o.oo.oo.oo.ooooooooooooooooX X ... . ", | ||
| 19 | " .oo.oo.oo.o.oo.ooo.ooooooooooooooooX X . . ", | ||
| 20 | " .ooooooooooooooooooooooooooooooooooX X . ", | ||
| 21 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X...... ", | ||
| 22 | " ", | ||
| 23 | " ", | ||
| 24 | " ", | ||
| 25 | " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ", | ||
| 26 | " X............................................ ", | ||
| 27 | " X.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX.OOOOX. ", | ||
| 28 | " X.O+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX.OX OX. ", | ||
| 29 | " X.O++OOO+OO+++OOOOOOOOOOOOOOOOOOOOOOOX.X ..X. ", | ||
| 30 | " X.O+O+O+OOO+O+OOOOOOOOOOOOOOOOOOOOOOOX.OOOOX. ", | ||
| 31 | " X.O++OOO+OO+++OOOOOOOOOOOOOOOOOOOOOOOX.OOOOX. ", | ||
| 32 | " X.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX.XXXXX. ", | ||
| 33 | " X.O.....X..........................OOX.X .X. ", | ||
| 34 | " X.OX...XXX.X.XX.XX.................OOX.X .X. ", | ||
| 35 | " X.OX.X..X..X.XX..XX.X..............OOX.X .X. ", | ||
| 36 | " X.O.X...X..X.X...X..X..............OOX.X .X. ", | ||
| 37 | " X.OOOOOOOOOOOOOOOOOOOOOOOO+OOOOOOOOOOX.X .X. ", | ||
| 38 | " X.OOOOOOOOO+OOO+OOOOO+OOOO+OOOOOOOOOOX.X .X. ", | ||
| 39 | " X.O+++OO+OO+O+OO++O++OO+OO+OOOOOOOOOOX.X...X. ", | ||
| 40 | " X.OO+OO++OO+O+OO+OOO+OO+O++OOOOOOOOOOX.OOOOX. ", | ||
| 41 | " X.OOOOOOOO+OOOOO++OO+OOOOOOOOOOOOOOOOX.OOOOX. ", | ||
| 42 | " X.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX.X .X. ", | ||
| 43 | " X.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX.O .OX. ", | ||
| 44 | " X.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX.OOOOX. ", | ||
| 45 | " X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXX. ", | ||
| 46 | " X............................................ ", | ||
| 47 | " ", | ||
| 48 | " ", | ||
| 49 | " "}; | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | #define drivea_width 32 | ||
| 2 | #define drivea_height 32 | ||
| 3 | static unsigned char drivea_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 5 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 6 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 7 | 0xf8, 0xff, 0xff, 0x1f, 0x08, 0x00, 0x00, 0x18, 0xa8, 0xaa, 0xaa, 0x1a, | ||
| 8 | 0x48, 0x55, 0xd5, 0x1d, 0xa8, 0xaa, 0xaa, 0x1b, 0x48, 0x55, 0x55, 0x1d, | ||
| 9 | 0xa8, 0xfa, 0xaf, 0x1a, 0xc8, 0xff, 0xff, 0x1d, 0xa8, 0xfa, 0xaf, 0x1a, | ||
| 10 | 0x48, 0x55, 0x55, 0x1d, 0xa8, 0xaa, 0xaa, 0x1a, 0x48, 0x55, 0x55, 0x1d, | ||
| 11 | 0xa8, 0xaa, 0xaa, 0x1a, 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f, | ||
| 12 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 13 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 14 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | /* XPM */ | ||
| 2 | static char * drivea_xpm[] = { | ||
| 3 | /* width height ncolors chars_per_pixel */ | ||
| 4 | "32 32 5 1", | ||
| 5 | /* colors */ | ||
| 6 | " s None c None", | ||
| 7 | ". c #000000000000", | ||
| 8 | "X c white", | ||
| 9 | "o c #c000c000c000", | ||
| 10 | "O c #800080008000", | ||
| 11 | /* pixels */ | ||
| 12 | " ", | ||
| 13 | " ", | ||
| 14 | " ", | ||
| 15 | " ", | ||
| 16 | " ", | ||
| 17 | " ", | ||
| 18 | " ", | ||
| 19 | " ", | ||
| 20 | " ", | ||
| 21 | " .......................... ", | ||
| 22 | " .XXXXXXXXXXXXXXXXXXXXXXXo. ", | ||
| 23 | " .XooooooooooooooooooooooO. ", | ||
| 24 | " .Xooooooooooooooooo..oooO. ", | ||
| 25 | " .Xooooooooooooooooo..oooO. ", | ||
| 26 | " .XooooooooooooooooooooooO. ", | ||
| 27 | " .Xoooooooo.......oooooooO. ", | ||
| 28 | " .Xoo...................oO. ", | ||
| 29 | " .Xoooooooo.......oooooooO. ", | ||
| 30 | " .XooooooooooooooooooooooO. ", | ||
| 31 | " .XooooooooooooooooooooooO. ", | ||
| 32 | " .XooooooooooooooooooooooO. ", | ||
| 33 | " .XooooooooooooooooooooooO. ", | ||
| 34 | " .oOOOOOOOOOOOOOOOOOOOOOOO. ", | ||
| 35 | " .......................... ", | ||
| 36 | " ", | ||
| 37 | " ", | ||
| 38 | " ", | ||
| 39 | " ", | ||
| 40 | " ", | ||
| 41 | " ", | ||
| 42 | " ", | ||
| 43 | " "}; | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | /* XPM */ | ||
| 2 | static char * exit_xpm[] = { | ||
| 3 | "50 40 5 1", | ||
| 4 | " s None c None", | ||
| 5 | ". c black", | ||
| 6 | "X c white", | ||
| 7 | "o c #000080800000", | ||
| 8 | "O c yellow", | ||
| 9 | " ", | ||
| 10 | " ", | ||
| 11 | " ", | ||
| 12 | " ", | ||
| 13 | " ", | ||
| 14 | " ", | ||
| 15 | " ", | ||
| 16 | " ", | ||
| 17 | " ", | ||
| 18 | " ....................................... ", | ||
| 19 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. ", | ||
| 20 | " .XoooooooooooooooooooooooooooooooooooX. ", | ||
| 21 | " .XoooooooooooooooooooooooooooooooooooX. ", | ||
| 22 | " .XoooooooooooooooooooooooOoooooooooooX. ", | ||
| 23 | " .XoooooooooooooooooooooooOOooooooooooX. ", | ||
| 24 | " .XoooooooooooooooooooooooOOOoooooooooX. ", | ||
| 25 | " .XoooooOOOOOOOOOOOOOOOOOOOOOOooooooooX. ", | ||
| 26 | " .XoooooOOOOOOOOOOOOOOOOOOOOOOOoooooooX. ", | ||
| 27 | " .XoooooOOOOOOOOOOOOOOOOOOOOOOOOooooooX. ", | ||
| 28 | " .XoooooOOOOOOOOOOOOOOOOOOOOOOOOOoooooX. ", | ||
| 29 | " .XoooooOOOOOOOOOOOOOOOOOOOOOOOOooooooX. ", | ||
| 30 | " .XoooooOOOOOOOOOOOOOOOOOOOOOOOoooooooX. ", | ||
| 31 | " .XoooooOOOOOOOOOOOOOOOOOOOOOOooooooooX. ", | ||
| 32 | " .XoooooooooooooooooooooooOOOoooooooooX. ", | ||
| 33 | " .XoooooooooooooooooooooooOOooooooooooX. ", | ||
| 34 | " .XoooooooooooooooooooooooOoooooooooooX. ", | ||
| 35 | " .XoooooooooooooooooooooooooooooooooooX. ", | ||
| 36 | " .XoooooooooooooooooooooooooooooooooooX. ", | ||
| 37 | " .XoooooooooooooooooooooooooooooooooooX. ", | ||
| 38 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. ", | ||
| 39 | " ....................................... ", | ||
| 40 | " ", | ||
| 41 | " ", | ||
| 42 | " ", | ||
| 43 | " ", | ||
| 44 | " ", | ||
| 45 | " ", | ||
| 46 | " ", | ||
| 47 | " ", | ||
| 48 | " "}; | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | #define filebox_width 32 | ||
| 2 | #define filebox_height 32 | ||
| 3 | static unsigned char filebox_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 5 | 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x3f, 0x04, 0x00, 0x00, 0x20, | ||
| 6 | 0xe4, 0xff, 0xff, 0x27, 0x24, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x24, | ||
| 7 | 0xe4, 0xff, 0xff, 0x27, 0x04, 0x00, 0x00, 0x20, 0xe4, 0x7f, 0xfe, 0x27, | ||
| 8 | 0x24, 0x50, 0x02, 0x25, 0x24, 0x40, 0x02, 0x24, 0x24, 0x50, 0x02, 0x25, | ||
| 9 | 0x24, 0x40, 0x02, 0x24, 0x24, 0x50, 0x02, 0x25, 0x24, 0x40, 0x02, 0x24, | ||
| 10 | 0x24, 0x50, 0x02, 0x25, 0xe4, 0x7f, 0xfe, 0x27, 0x04, 0x00, 0x00, 0x20, | ||
| 11 | 0xe4, 0xff, 0xff, 0x27, 0x24, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x24, | ||
| 12 | 0xe4, 0xff, 0xff, 0x27, 0x04, 0x00, 0x00, 0x20, 0xfc, 0xff, 0xff, 0x3f, | ||
| 13 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 14 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* XPM */ | ||
| 2 | static char * filebox_xpm[] = { | ||
| 3 | "50 40 6 1", | ||
| 4 | " s None c None", | ||
| 5 | ". c white", | ||
| 6 | "X c gray80", | ||
| 7 | "o c black", | ||
| 8 | "O c #FFFF80808080", | ||
| 9 | "+ c gray70", | ||
| 10 | " ", | ||
| 11 | " ", | ||
| 12 | " ", | ||
| 13 | " ............................................ ", | ||
| 14 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 15 | " .XXooXooXoXooXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 16 | " .XXooXooXoXooXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 17 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 18 | " .XXooooooooooooooooooooooooooooooooooooo.XXo ", | ||
| 19 | " .XXoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XXo ", | ||
| 20 | " .XXoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.XXo ", | ||
| 21 | " .XX......................................XXo ", | ||
| 22 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 23 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 24 | " .XXoooooooooooooooo.XXXXoooooooooooooooo.XXo ", | ||
| 25 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 26 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 27 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 28 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 29 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 30 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 31 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 32 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 33 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 34 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 35 | " .XXo+++++++++++++++.XXXXo+++++++++++++++.XXo ", | ||
| 36 | " .XX.................XXXX.................XXo ", | ||
| 37 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 38 | " .XXooXooXoXooXoXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 39 | " .XXooXooXoXooXoXooXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 40 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 41 | " .XXoooooooooooooooooooooooooooooooooooooo.Xo ", | ||
| 42 | " .XXoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.Xo ", | ||
| 43 | " .XXoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.Xo ", | ||
| 44 | " .XX.......................................Xo ", | ||
| 45 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 46 | " .ooooooooooooooooooooooooooooooooooooooooooo ", | ||
| 47 | " ", | ||
| 48 | " ", | ||
| 49 | " "}; | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | #define italic_width 16 | ||
| 2 | #define italic_height 16 | ||
| 3 | static unsigned char italic_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x80, 0x3f, 0x00, 0x06, 0x00, 0x06, | ||
| 5 | 0x00, 0x03, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0xc0, 0x00, 0xc0, 0x00, | ||
| 6 | 0x60, 0x00, 0x60, 0x00, 0xfc, 0x01, 0xfc, 0x01}; | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | #define justify_width 16 | ||
| 2 | #define justify_height 16 | ||
| 3 | static unsigned char justify_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xdb, 0x00, 0x00, 0x7c, 0xdb, | ||
| 5 | 0x00, 0x00, 0xbc, 0xf7, 0x00, 0x00, 0xdc, 0xde, 0x00, 0x00, 0x6c, 0xdf, | ||
| 6 | 0x00, 0x00, 0x6c, 0xef, 0x00, 0x00, 0xdc, 0xdf}; | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | #define leftj_width 16 | ||
| 2 | #define leftj_height 16 | ||
| 3 | static unsigned char leftj_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x6d, 0x00, 0x00, 0xdc, 0x01, | ||
| 5 | 0x00, 0x00, 0xec, 0x0e, 0x00, 0x00, 0xfc, 0x7e, 0x00, 0x00, 0xdc, 0x03, | ||
| 6 | 0x00, 0x00, 0x6c, 0x3b, 0x00, 0x00, 0x6c, 0x1f}; | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | #define netw_width 32 | ||
| 2 | #define netw_height 32 | ||
| 3 | static unsigned char netw_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x02, 0x40, | ||
| 5 | 0x00, 0x00, 0xfa, 0x5f, 0x00, 0x00, 0x0a, 0x50, 0x00, 0x00, 0x0a, 0x52, | ||
| 6 | 0x00, 0x00, 0x0a, 0x52, 0x00, 0x00, 0x8a, 0x51, 0x00, 0x00, 0x0a, 0x50, | ||
| 7 | 0x00, 0x00, 0x4a, 0x50, 0x00, 0x00, 0x0a, 0x50, 0x00, 0x00, 0x0a, 0x50, | ||
| 8 | 0x00, 0x00, 0xfa, 0x5f, 0x00, 0x00, 0x02, 0x40, 0xfe, 0x7f, 0x52, 0x55, | ||
| 9 | 0x02, 0x40, 0xaa, 0x6a, 0xfa, 0x5f, 0xfe, 0x7f, 0x0a, 0x50, 0xfe, 0x7f, | ||
| 10 | 0x0a, 0x52, 0x80, 0x00, 0x0a, 0x52, 0x80, 0x00, 0x8a, 0x51, 0x80, 0x00, | ||
| 11 | 0x0a, 0x50, 0x80, 0x00, 0x4a, 0x50, 0x80, 0x00, 0x0a, 0x50, 0xe0, 0x03, | ||
| 12 | 0x0a, 0x50, 0x20, 0x02, 0xfa, 0xdf, 0x3f, 0x03, 0x02, 0x40, 0xa0, 0x02, | ||
| 13 | 0x52, 0x55, 0xe0, 0x03, 0xaa, 0x6a, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, | ||
| 14 | 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* XPM */ | ||
| 2 | static char * netw_xpm[] = { | ||
| 3 | /* width height ncolors chars_per_pixel */ | ||
| 4 | "32 32 7 1", | ||
| 5 | /* colors */ | ||
| 6 | " s None c None", | ||
| 7 | ". c #000000000000", | ||
| 8 | "X c white", | ||
| 9 | "o c #c000c000c000", | ||
| 10 | "O c #404040", | ||
| 11 | "+ c blue", | ||
| 12 | "@ c red", | ||
| 13 | /* pixels */ | ||
| 14 | " ", | ||
| 15 | " .............. ", | ||
| 16 | " .XXXXXXXXXXXX. ", | ||
| 17 | " .XooooooooooO. ", | ||
| 18 | " .Xo.......XoO. ", | ||
| 19 | " .Xo.++++o+XoO. ", | ||
| 20 | " .Xo.++++o+XoO. ", | ||
| 21 | " .Xo.++oo++XoO. ", | ||
| 22 | " .Xo.++++++XoO. ", | ||
| 23 | " .Xo.+o++++XoO. ", | ||
| 24 | " .Xo.++++++XoO. ", | ||
| 25 | " .Xo.XXXXXXXoO. ", | ||
| 26 | " .XooooooooooO. ", | ||
| 27 | " .Xo@ooo....oO. ", | ||
| 28 | " .............. .XooooooooooO. ", | ||
| 29 | " .XXXXXXXXXXXX. .XooooooooooO. ", | ||
| 30 | " .XooooooooooO. .OOOOOOOOOOOO. ", | ||
| 31 | " .Xo.......XoO. .............. ", | ||
| 32 | " .Xo.++++o+XoO. @ ", | ||
| 33 | " .Xo.++++o+XoO. @ ", | ||
| 34 | " .Xo.++oo++XoO. @ ", | ||
| 35 | " .Xo.++++++XoO. @ ", | ||
| 36 | " .Xo.+o++++XoO. @ ", | ||
| 37 | " .Xo.++++++XoO. ..... ", | ||
| 38 | " .Xo.XXXXXXXoO. .XXX. ", | ||
| 39 | " .XooooooooooO.@@@@@@.X O. ", | ||
| 40 | " .Xo@ooo....oO. .OOO. ", | ||
| 41 | " .XooooooooooO. ..... ", | ||
| 42 | " .XooooooooooO. ", | ||
| 43 | " .OOOOOOOOOOOO. ", | ||
| 44 | " .............. ", | ||
| 45 | " "}; | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | /* XPM */ | ||
| 2 | static char * optmenu_xpm[] = { | ||
| 3 | "50 40 5 1", | ||
| 4 | " s None c None", | ||
| 5 | ". c white", | ||
| 6 | "X c gray80", | ||
| 7 | "o c gray50", | ||
| 8 | "O c black", | ||
| 9 | " ", | ||
| 10 | " ", | ||
| 11 | " .............................. ", | ||
| 12 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 13 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 14 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 15 | " .XXXOXOXXOXXOXXXXOOXXXXXXXXXXo ", | ||
| 16 | " .XXXOXOXXOXOXXXOXXOXXXXXXXXXXo ", | ||
| 17 | " .XXXXOXXOXXOXXXOXXXOXXXXXXXXXo ", | ||
| 18 | " .XXXXOXXXOXXOOXXOXOXXXXXXXXXXo ", | ||
| 19 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 20 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo.............o ", | ||
| 21 | " .............................o o ", | ||
| 22 | " ..XXXOXXXXXOXXXXXXXXOXXXXXXXOo o ", | ||
| 23 | " ..XXOXOXOXXOXOXXXOXXOXXXXXXXOo ...... o ", | ||
| 24 | " ..XXXOXXXOXXOXXXOXXXOXXXXXXXOo . o o ", | ||
| 25 | " ..XXOXXXOXXXOXOXXOXXOXXXXXXXOo . o o ", | ||
| 26 | " ..XXXXXXXXXXXXXXXXXXXXXXXXXXOo .ooooo o ", | ||
| 27 | " .OOOOOOOOOOOOOOOOOOOOOOOOOOOOo o ", | ||
| 28 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo o ", | ||
| 29 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXooooooooooooooo ", | ||
| 30 | " .XXXXOXXXXXOXXXXXXXXXXXXXXXXXo ", | ||
| 31 | " .XXXOXXXXXXXXXOXXXXXXXXXXXXXXo ", | ||
| 32 | " .XXXXOXXOXXOXOXOXXXXXXXXXXXXXo ", | ||
| 33 | " .XXXXXOXXOXOXXXXXXXXXXXXXXXXXo ", | ||
| 34 | " .XXXXXXXXXXXXXOXXXXXXXXXXXXXXo ", | ||
| 35 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 36 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 37 | " .XXXOXOXXXXXXXOXOXXXXXOXXXXXXo ", | ||
| 38 | " .XXXXXOXOXOXXOXXXXXOXXOXXXXXXo ", | ||
| 39 | " .XXXXOXXOXOXOXXXOXOXOXXOXXXXXo ", | ||
| 40 | " .XXXOXXXXOXXOXXXOXXOXXXXOXXXXo ", | ||
| 41 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 42 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 43 | " .XXXXXXXXXXXXXXXXXXXXXXXXXXXXo ", | ||
| 44 | " oooooooooooooooooooooooooooooo ", | ||
| 45 | " ", | ||
| 46 | " ", | ||
| 47 | " ", | ||
| 48 | " "}; | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | #define rightj_width 16 | ||
| 2 | #define rightj_height 16 | ||
| 3 | static unsigned char rightj_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xdb, 0x00, 0x00, 0x70, 0xdb, | ||
| 5 | 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0xd8, 0xde, 0x00, 0x00, 0xc0, 0xdd, | ||
| 6 | 0x00, 0x00, 0xa0, 0xef, 0x00, 0x00, 0xd8, 0xde}; | ||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* XPM */ | ||
| 2 | static char * select_xpm[] = { | ||
| 3 | "50 40 9 1", | ||
| 4 | " s None c None", | ||
| 5 | ". c black", | ||
| 6 | "X c gray95", | ||
| 7 | "o c gray50", | ||
| 8 | "O c gray70", | ||
| 9 | "+ c navy", | ||
| 10 | "@ c #000080800000", | ||
| 11 | "# c #808000000000", | ||
| 12 | "$ c white", | ||
| 13 | " ", | ||
| 14 | " ", | ||
| 15 | " ", | ||
| 16 | " ", | ||
| 17 | " ", | ||
| 18 | " ", | ||
| 19 | " ", | ||
| 20 | " ", | ||
| 21 | " ", | ||
| 22 | " .............................................. ", | ||
| 23 | " .XXXXXXXXXXooooooooooooXXXXXXXXXXXoXXXXXXXXXX. ", | ||
| 24 | " .X ooOOOOOOOOOOXX oX o. ", | ||
| 25 | " .X ooOOOOOOOOOOXX oX o. ", | ||
| 26 | " .X ++++ ooOOOOOOOOOOXX ... oX @ o. ", | ||
| 27 | " .X +++++ ooOOOOOOOOOOXX . . oX @@@ o. ", | ||
| 28 | " .X +++ + ooOOOOOOOOOOXX . . oX @ @ o. ", | ||
| 29 | " .X + + ooOO#####OOOXX . . oX @ @ o. ", | ||
| 30 | " .X + + ooOO#OOO##OOXX . oX @ @ o. ", | ||
| 31 | " .X + + ooO##OOOO##OXX . oX @ @ o. ", | ||
| 32 | " .X ++ ++ ooO###OOO#OOXX . oX @ @ o. ", | ||
| 33 | " .X +++++++ ooO#######OOXX . oX @ @ o. ", | ||
| 34 | " .X + + ooO##O#OO#OOXX . oX @ @ o. ", | ||
| 35 | " .X + ++ ooO##OOOOO#OXX . . oX @ @ o. ", | ||
| 36 | " .X + + ooOO#OOOOO#OXX . . oX @ @@ o. ", | ||
| 37 | " .X + ++ ooOO#OOOOO#OXX .... oX @@@@@ o. ", | ||
| 38 | " .X ooOO######OOXX oX o. ", | ||
| 39 | " .X ooOOOOOOOOOOXX $oX o. ", | ||
| 40 | " .XoooooooooooXXXXXXXXXXXoooooooooooXooooooooo. ", | ||
| 41 | " .............................................. ", | ||
| 42 | " ", | ||
| 43 | " ", | ||
| 44 | " ", | ||
| 45 | " ", | ||
| 46 | " ", | ||
| 47 | " ", | ||
| 48 | " ", | ||
| 49 | " ", | ||
| 50 | " ", | ||
| 51 | " ", | ||
| 52 | " "}; | ||
Binary file not shown.
| @@ -0,0 +1,6 @@ | |||
| 1 | #define underline_width 16 | ||
| 2 | #define underline_height 16 | ||
| 3 | static unsigned char underline_bits[] = { | ||
| 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x38, 0x1c, | ||
| 5 | 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x70, 0x0e, | ||
| 6 | 0xf0, 0x0f, 0xe0, 0x07, 0x00, 0x00, 0xf8, 0x1f}; | ||
| @@ -0,0 +1,28 @@ | |||
| 1 | ### | ||
| 2 | import Tix as tk | ||
| 3 | from pprint import pprint | ||
| 4 | |||
| 5 | r= tk.Tk() | ||
| 6 | r.title("test") | ||
| 7 | |||
| 8 | l=tk.Label(r, name="a_label") | ||
| 9 | l.pack() | ||
| 10 | |||
| 11 | class MyGrid(tk.Grid): | ||
| 12 | def __init__(self, *args, **kwargs): | ||
| 13 | kwargs['editnotify']= self.editnotify | ||
| 14 | tk.Grid.__init__(self, *args, **kwargs) | ||
| 15 | def editnotify(self, x, y): | ||
| 16 | return True | ||
| 17 | |||
| 18 | g = MyGrid(r, name="a_grid", | ||
| 19 | selectunit="cell") | ||
| 20 | g.pack(fill=tk.BOTH) | ||
| 21 | for x in xrange(5): | ||
| 22 | for y in xrange(5): | ||
| 23 | g.set(x,y,text=str((x,y))) | ||
| 24 | |||
| 25 | c = tk.Button(r, text="Close", command=r.destroy) | ||
| 26 | c.pack() | ||
| 27 | |||
| 28 | tk.mainloop() | ||
| @@ -0,0 +1,68 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: Balloon.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixBalloon widget, which provides | ||
| 14 | # a interesting way to give help tips about elements in your user interface. | ||
| 15 | # Your can display the help message in a "balloon" and a status bar widget. | ||
| 16 | # | ||
| 17 | |||
| 18 | import Tix | ||
| 19 | |||
| 20 | TCL_ALL_EVENTS = 0 | ||
| 21 | |||
| 22 | def RunSample (root): | ||
| 23 | balloon = DemoBalloon(root) | ||
| 24 | balloon.mainloop() | ||
| 25 | balloon.destroy() | ||
| 26 | |||
| 27 | class DemoBalloon: | ||
| 28 | def __init__(self, w): | ||
| 29 | self.root = w | ||
| 30 | self.exit = -1 | ||
| 31 | |||
| 32 | z = w.winfo_toplevel() | ||
| 33 | z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd()) | ||
| 34 | |||
| 35 | status = Tix.Label(w, width=40, relief=Tix.SUNKEN, bd=1) | ||
| 36 | status.pack(side=Tix.BOTTOM, fill=Tix.Y, padx=2, pady=1) | ||
| 37 | |||
| 38 | # Create two mysterious widgets that need balloon help | ||
| 39 | button1 = Tix.Button(w, text='Something Unexpected', | ||
| 40 | command=self.quitcmd) | ||
| 41 | button2 = Tix.Button(w, text='Something Else Unexpected') | ||
| 42 | button2['command'] = lambda w=button2: w.destroy() | ||
| 43 | button1.pack(side=Tix.TOP, expand=1) | ||
| 44 | button2.pack(side=Tix.TOP, expand=1) | ||
| 45 | |||
| 46 | # Create the balloon widget and associate it with the widgets that we want | ||
| 47 | # to provide tips for: | ||
| 48 | b = Tix.Balloon(w, statusbar=status) | ||
| 49 | |||
| 50 | b.bind_widget(button1, balloonmsg='Close Window', | ||
| 51 | statusmsg='Press this button to close this window') | ||
| 52 | b.bind_widget(button2, balloonmsg='Self-destruct button', | ||
| 53 | statusmsg='Press this button and it will destroy itself') | ||
| 54 | |||
| 55 | def quitcmd (self): | ||
| 56 | self.exit = 0 | ||
| 57 | |||
| 58 | def mainloop(self): | ||
| 59 | foundEvent = 1 | ||
| 60 | while self.exit < 0 and foundEvent > 0: | ||
| 61 | foundEvent = self.root.tk.dooneevent(TCL_ALL_EVENTS) | ||
| 62 | |||
| 63 | def destroy (self): | ||
| 64 | self.root.destroy() | ||
| 65 | |||
| 66 | if __name__ == '__main__': | ||
| 67 | root = Tix.Tk() | ||
| 68 | RunSample(root) | ||
| @@ -0,0 +1,44 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: BtnBox.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixButtonBox widget, which is a | ||
| 14 | # group of TK buttons. You can use it to manage the buttons in a dialog box, | ||
| 15 | # for example. | ||
| 16 | # | ||
| 17 | |||
| 18 | import Tix | ||
| 19 | |||
| 20 | def RunSample(w): | ||
| 21 | # Create the label on the top of the dialog box | ||
| 22 | # | ||
| 23 | top = Tix.Label(w, padx=20, pady=10, bd=1, relief=Tix.RAISED, | ||
| 24 | anchor=Tix.CENTER, text='This dialog box is\n a demonstration of the\n tixButtonBox widget') | ||
| 25 | |||
| 26 | # Create the button box and add a few buttons in it. Set the | ||
| 27 | # -width of all the buttons to the same value so that they | ||
| 28 | # appear in the same size. | ||
| 29 | # | ||
| 30 | # Note that the -text, -underline, -command and -width options are all | ||
| 31 | # standard options of the button widgets. | ||
| 32 | # | ||
| 33 | box = Tix.ButtonBox(w, orientation=Tix.HORIZONTAL) | ||
| 34 | box.add('ok', text='OK', underline=0, width=5, | ||
| 35 | command=lambda w=w: w.destroy()) | ||
| 36 | box.add('close', text='Cancel', underline=0, width=5, | ||
| 37 | command=lambda w=w: w.destroy()) | ||
| 38 | box.pack(side=Tix.BOTTOM, fill=Tix.X) | ||
| 39 | top.pack(side=Tix.TOP, fill=Tix.BOTH, expand=1) | ||
| 40 | |||
| 41 | if __name__ == '__main__': | ||
| 42 | root = Tix.Tk() | ||
| 43 | RunSample(root) | ||
| 44 | root.mainloop() | ||
| @@ -0,0 +1,196 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: CmpImg.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the compound images: it uses compound | ||
| 14 | # images to display a text string together with a pixmap inside | ||
| 15 | # buttons | ||
| 16 | # | ||
| 17 | |||
| 18 | import Tix | ||
| 19 | |||
| 20 | network_pixmap = """/* XPM */ | ||
| 21 | static char * netw_xpm[] = { | ||
| 22 | /* width height ncolors chars_per_pixel */ | ||
| 23 | "32 32 7 1", | ||
| 24 | /* colors */ | ||
| 25 | " s None c None", | ||
| 26 | ". c #000000000000", | ||
| 27 | "X c white", | ||
| 28 | "o c #c000c000c000", | ||
| 29 | "O c #404040", | ||
| 30 | "+ c blue", | ||
| 31 | "@ c red", | ||
| 32 | /* pixels */ | ||
| 33 | " ", | ||
| 34 | " .............. ", | ||
| 35 | " .XXXXXXXXXXXX. ", | ||
| 36 | " .XooooooooooO. ", | ||
| 37 | " .Xo.......XoO. ", | ||
| 38 | " .Xo.++++o+XoO. ", | ||
| 39 | " .Xo.++++o+XoO. ", | ||
| 40 | " .Xo.++oo++XoO. ", | ||
| 41 | " .Xo.++++++XoO. ", | ||
| 42 | " .Xo.+o++++XoO. ", | ||
| 43 | " .Xo.++++++XoO. ", | ||
| 44 | " .Xo.XXXXXXXoO. ", | ||
| 45 | " .XooooooooooO. ", | ||
| 46 | " .Xo@ooo....oO. ", | ||
| 47 | " .............. .XooooooooooO. ", | ||
| 48 | " .XXXXXXXXXXXX. .XooooooooooO. ", | ||
| 49 | " .XooooooooooO. .OOOOOOOOOOOO. ", | ||
| 50 | " .Xo.......XoO. .............. ", | ||
| 51 | " .Xo.++++o+XoO. @ ", | ||
| 52 | " .Xo.++++o+XoO. @ ", | ||
| 53 | " .Xo.++oo++XoO. @ ", | ||
| 54 | " .Xo.++++++XoO. @ ", | ||
| 55 | " .Xo.+o++++XoO. @ ", | ||
| 56 | " .Xo.++++++XoO. ..... ", | ||
| 57 | " .Xo.XXXXXXXoO. .XXX. ", | ||
| 58 | " .XooooooooooO.@@@@@@.X O. ", | ||
| 59 | " .Xo@ooo....oO. .OOO. ", | ||
| 60 | " .XooooooooooO. ..... ", | ||
| 61 | " .XooooooooooO. ", | ||
| 62 | " .OOOOOOOOOOOO. ", | ||
| 63 | " .............. ", | ||
| 64 | " "}; | ||
| 65 | """ | ||
| 66 | |||
| 67 | hard_disk_pixmap = """/* XPM */ | ||
| 68 | static char * drivea_xpm[] = { | ||
| 69 | /* width height ncolors chars_per_pixel */ | ||
| 70 | "32 32 5 1", | ||
| 71 | /* colors */ | ||
| 72 | " s None c None", | ||
| 73 | ". c #000000000000", | ||
| 74 | "X c white", | ||
| 75 | "o c #c000c000c000", | ||
| 76 | "O c #800080008000", | ||
| 77 | /* pixels */ | ||
| 78 | " ", | ||
| 79 | " ", | ||
| 80 | " ", | ||
| 81 | " ", | ||
| 82 | " ", | ||
| 83 | " ", | ||
| 84 | " ", | ||
| 85 | " ", | ||
| 86 | " ", | ||
| 87 | " .......................... ", | ||
| 88 | " .XXXXXXXXXXXXXXXXXXXXXXXo. ", | ||
| 89 | " .XooooooooooooooooooooooO. ", | ||
| 90 | " .Xooooooooooooooooo..oooO. ", | ||
| 91 | " .Xooooooooooooooooo..oooO. ", | ||
| 92 | " .XooooooooooooooooooooooO. ", | ||
| 93 | " .Xoooooooo.......oooooooO. ", | ||
| 94 | " .Xoo...................oO. ", | ||
| 95 | " .Xoooooooo.......oooooooO. ", | ||
| 96 | " .XooooooooooooooooooooooO. ", | ||
| 97 | " .XooooooooooooooooooooooO. ", | ||
| 98 | " .XooooooooooooooooooooooO. ", | ||
| 99 | " .XooooooooooooooooooooooO. ", | ||
| 100 | " .oOOOOOOOOOOOOOOOOOOOOOOO. ", | ||
| 101 | " .......................... ", | ||
| 102 | " ", | ||
| 103 | " ", | ||
| 104 | " ", | ||
| 105 | " ", | ||
| 106 | " ", | ||
| 107 | " ", | ||
| 108 | " ", | ||
| 109 | " "}; | ||
| 110 | """ | ||
| 111 | |||
| 112 | network_bitmap = """ | ||
| 113 | #define netw_width 32 | ||
| 114 | #define netw_height 32 | ||
| 115 | static unsigned char netw_bits[] = { | ||
| 116 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x02, 0x40, | ||
| 117 | 0x00, 0x00, 0xfa, 0x5f, 0x00, 0x00, 0x0a, 0x50, 0x00, 0x00, 0x0a, 0x52, | ||
| 118 | 0x00, 0x00, 0x0a, 0x52, 0x00, 0x00, 0x8a, 0x51, 0x00, 0x00, 0x0a, 0x50, | ||
| 119 | 0x00, 0x00, 0x4a, 0x50, 0x00, 0x00, 0x0a, 0x50, 0x00, 0x00, 0x0a, 0x50, | ||
| 120 | 0x00, 0x00, 0xfa, 0x5f, 0x00, 0x00, 0x02, 0x40, 0xfe, 0x7f, 0x52, 0x55, | ||
| 121 | 0x02, 0x40, 0xaa, 0x6a, 0xfa, 0x5f, 0xfe, 0x7f, 0x0a, 0x50, 0xfe, 0x7f, | ||
| 122 | 0x0a, 0x52, 0x80, 0x00, 0x0a, 0x52, 0x80, 0x00, 0x8a, 0x51, 0x80, 0x00, | ||
| 123 | 0x0a, 0x50, 0x80, 0x00, 0x4a, 0x50, 0x80, 0x00, 0x0a, 0x50, 0xe0, 0x03, | ||
| 124 | 0x0a, 0x50, 0x20, 0x02, 0xfa, 0xdf, 0x3f, 0x03, 0x02, 0x40, 0xa0, 0x02, | ||
| 125 | 0x52, 0x55, 0xe0, 0x03, 0xaa, 0x6a, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, | ||
| 126 | 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
| 127 | """ | ||
| 128 | |||
| 129 | hard_disk_bitmap = """ | ||
| 130 | #define drivea_width 32 | ||
| 131 | #define drivea_height 32 | ||
| 132 | static unsigned char drivea_bits[] = { | ||
| 133 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 135 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 136 | 0xf8, 0xff, 0xff, 0x1f, 0x08, 0x00, 0x00, 0x18, 0xa8, 0xaa, 0xaa, 0x1a, | ||
| 137 | 0x48, 0x55, 0xd5, 0x1d, 0xa8, 0xaa, 0xaa, 0x1b, 0x48, 0x55, 0x55, 0x1d, | ||
| 138 | 0xa8, 0xfa, 0xaf, 0x1a, 0xc8, 0xff, 0xff, 0x1d, 0xa8, 0xfa, 0xaf, 0x1a, | ||
| 139 | 0x48, 0x55, 0x55, 0x1d, 0xa8, 0xaa, 0xaa, 0x1a, 0x48, 0x55, 0x55, 0x1d, | ||
| 140 | 0xa8, 0xaa, 0xaa, 0x1a, 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f, | ||
| 141 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 142 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 143 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
| 144 | """ | ||
| 145 | |||
| 146 | def RunSample(w): | ||
| 147 | w.img0 = Tix.Image('pixmap', data=network_pixmap) | ||
| 148 | if not w.img0: | ||
| 149 | w.img0 = Tix.Image('bitmap', data=network_bitmap) | ||
| 150 | w.img1 = Tix.Image('pixmap', data=hard_disk_pixmap) | ||
| 151 | if not w.img0: | ||
| 152 | w.img1 = Tix.Image('bitmap', data=hard_disk_bitmap) | ||
| 153 | |||
| 154 | hdd = Tix.Button(w, padx=4, pady=1, width=120) | ||
| 155 | net = Tix.Button(w, padx=4, pady=1, width=120) | ||
| 156 | |||
| 157 | # Create the first image: we create a line, then put a string, | ||
| 158 | # a space and a image into this line, from left to right. | ||
| 159 | # The result: we have a one-line image that consists of three | ||
| 160 | # individual items | ||
| 161 | # | ||
| 162 | # The tk.calls should be methods in Tix ... | ||
| 163 | w.hdd_img = Tix.Image('compound', window=hdd) | ||
| 164 | w.hdd_img.tk.call(str(w.hdd_img), 'add', 'line') | ||
| 165 | w.hdd_img.tk.call(str(w.hdd_img), 'add', 'text', '-text', 'Hard Disk', | ||
| 166 | '-underline', '0') | ||
| 167 | w.hdd_img.tk.call(str(w.hdd_img), 'add', 'space', '-width', '7') | ||
| 168 | w.hdd_img.tk.call(str(w.hdd_img), 'add', 'image', '-image', w.img1) | ||
| 169 | |||
| 170 | # Put this image into the first button | ||
| 171 | # | ||
| 172 | hdd['image'] = w.hdd_img | ||
| 173 | |||
| 174 | # Next button | ||
| 175 | w.net_img = Tix.Image('compound', window=net) | ||
| 176 | w.net_img.tk.call(str(w.net_img), 'add', 'line') | ||
| 177 | w.net_img.tk.call(str(w.net_img), 'add', 'text', '-text', 'Network', | ||
| 178 | '-underline', '0') | ||
| 179 | w.net_img.tk.call(str(w.net_img), 'add', 'space', '-width', '7') | ||
| 180 | w.net_img.tk.call(str(w.net_img), 'add', 'image', '-image', w.img0) | ||
| 181 | |||
| 182 | # Put this image into the first button | ||
| 183 | # | ||
| 184 | net['image'] = w.net_img | ||
| 185 | |||
| 186 | close = Tix.Button(w, pady=1, text='Close', | ||
| 187 | command=lambda w=w: w.destroy()) | ||
| 188 | |||
| 189 | hdd.pack(side=Tix.LEFT, padx=10, pady=10, fill=Tix.Y, expand=1) | ||
| 190 | net.pack(side=Tix.LEFT, padx=10, pady=10, fill=Tix.Y, expand=1) | ||
| 191 | close.pack(side=Tix.LEFT, padx=10, pady=10, fill=Tix.Y, expand=1) | ||
| 192 | |||
| 193 | if __name__ == '__main__': | ||
| 194 | root = Tix.Tk() | ||
| 195 | RunSample(root) | ||
| 196 | root.mainloop() | ||
| @@ -0,0 +1,102 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: ComboBox.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixComboBox widget, which is close | ||
| 14 | # to the MS Window Combo Box control. | ||
| 15 | # | ||
| 16 | import Tix | ||
| 17 | |||
| 18 | def RunSample(w): | ||
| 19 | global demo_month, demo_year | ||
| 20 | |||
| 21 | top = Tix.Frame(w, bd=1, relief=Tix.RAISED) | ||
| 22 | |||
| 23 | demo_month = Tix.StringVar() | ||
| 24 | demo_year = Tix.StringVar() | ||
| 25 | |||
| 26 | # $w.top.a is a drop-down combo box. It is not editable -- who wants | ||
| 27 | # to invent new months? | ||
| 28 | # | ||
| 29 | # [Hint] The -options switch sets the options of the subwidgets. | ||
| 30 | # [Hint] We set the label.width subwidget option of both comboboxes to | ||
| 31 | # be 10 so that their labels appear to be aligned. | ||
| 32 | # | ||
| 33 | a = Tix.ComboBox(top, label="Month: ", dropdown=1, | ||
| 34 | command=select_month, editable=0, variable=demo_month, | ||
| 35 | options='listbox.height 6 label.width 10 label.anchor e') | ||
| 36 | |||
| 37 | # $w.top.b is a non-drop-down combo box. It is not editable: we provide | ||
| 38 | # four choices for the user, but he can enter an alternative year if he | ||
| 39 | # wants to. | ||
| 40 | # | ||
| 41 | # [Hint] Use the padY and anchor options of the label subwidget to | ||
| 42 | # align the label with the entry subwidget. | ||
| 43 | # [Hint] Notice that you should use padY (the NAME of the option) and not | ||
| 44 | # pady (the SWITCH of the option). | ||
| 45 | # | ||
| 46 | b = Tix.ComboBox(top, label="Year: ", dropdown=0, | ||
| 47 | command=select_year, editable=1, variable=demo_year, | ||
| 48 | options='listbox.height 4 label.padY 5 label.width 10 label.anchor ne') | ||
| 49 | |||
| 50 | a.pack(side=Tix.TOP, anchor=Tix.W) | ||
| 51 | b.pack(side=Tix.TOP, anchor=Tix.W) | ||
| 52 | |||
| 53 | a.insert(Tix.END, 'January') | ||
| 54 | a.insert(Tix.END, 'February') | ||
| 55 | a.insert(Tix.END, 'March') | ||
| 56 | a.insert(Tix.END, 'April') | ||
| 57 | a.insert(Tix.END, 'May') | ||
| 58 | a.insert(Tix.END, 'June') | ||
| 59 | a.insert(Tix.END, 'July') | ||
| 60 | a.insert(Tix.END, 'August') | ||
| 61 | a.insert(Tix.END, 'September') | ||
| 62 | a.insert(Tix.END, 'October') | ||
| 63 | a.insert(Tix.END, 'November') | ||
| 64 | a.insert(Tix.END, 'December') | ||
| 65 | |||
| 66 | b.insert(Tix.END, '1992') | ||
| 67 | b.insert(Tix.END, '1993') | ||
| 68 | b.insert(Tix.END, '1994') | ||
| 69 | b.insert(Tix.END, '1995') | ||
| 70 | b.insert(Tix.END, '1996') | ||
| 71 | |||
| 72 | # Use "tixSetSilent" to set the values of the combo box if you | ||
| 73 | # don't want your -command procedures (cbx:select_month and | ||
| 74 | # cbx:select_year) to be called. | ||
| 75 | # | ||
| 76 | a.set_silent('January') | ||
| 77 | b.set_silent('1995') | ||
| 78 | |||
| 79 | box = Tix.ButtonBox(w, orientation=Tix.HORIZONTAL) | ||
| 80 | box.add('ok', text='Ok', underline=0, width=6, | ||
| 81 | command=lambda w=w: ok_command(w)) | ||
| 82 | box.add('cancel', text='Cancel', underline=0, width=6, | ||
| 83 | command=lambda w=w: w.destroy()) | ||
| 84 | box.pack(side=Tix.BOTTOM, fill=Tix.X) | ||
| 85 | top.pack(side=Tix.TOP, fill=Tix.BOTH, expand=1) | ||
| 86 | |||
| 87 | def select_month(event=None): | ||
| 88 | # tixDemo:Status "Month = %s" % demo_month.get() | ||
| 89 | pass | ||
| 90 | |||
| 91 | def select_year(event=None): | ||
| 92 | # tixDemo:Status "Year = %s" % demo_year.get() | ||
| 93 | pass | ||
| 94 | |||
| 95 | def ok_command(w): | ||
| 96 | # tixDemo:Status "Month = %s, Year= %s" % (demo_month.get(), demo_year.get()) | ||
| 97 | w.destroy() | ||
| 98 | |||
| 99 | if __name__ == '__main__': | ||
| 100 | root = Tix.Tk() | ||
| 101 | RunSample(root) | ||
| 102 | root.mainloop() | ||
| @@ -0,0 +1,122 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: Control.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixControl widget -- it is an | ||
| 14 | # entry widget with up/down arrow buttons. You can use the arrow buttons | ||
| 15 | # to adjust the value inside the entry widget. | ||
| 16 | # | ||
| 17 | # This example program uses three Control widgets. One lets you select | ||
| 18 | # integer values; one lets you select floating point values and the last | ||
| 19 | # one lets you select a few names. | ||
| 20 | |||
| 21 | import Tix | ||
| 22 | |||
| 23 | TCL_ALL_EVENTS = 0 | ||
| 24 | |||
| 25 | def RunSample (root): | ||
| 26 | control = DemoControl(root) | ||
| 27 | control.mainloop() | ||
| 28 | control.destroy() | ||
| 29 | |||
| 30 | class DemoControl: | ||
| 31 | def __init__(self, w): | ||
| 32 | self.root = w | ||
| 33 | self.exit = -1 | ||
| 34 | |||
| 35 | global demo_maker, demo_thrust, demo_num_engines | ||
| 36 | |||
| 37 | demo_maker = Tix.StringVar() | ||
| 38 | demo_thrust = Tix.DoubleVar() | ||
| 39 | demo_num_engines = Tix.IntVar() | ||
| 40 | demo_maker.set('P&W') | ||
| 41 | demo_thrust.set(20000.0) | ||
| 42 | demo_num_engines.set(2) | ||
| 43 | |||
| 44 | top = Tix.Frame(w, bd=1, relief=Tix.RAISED) | ||
| 45 | |||
| 46 | # $w.top.a allows only integer values | ||
| 47 | # | ||
| 48 | # [Hint] The -options switch sets the options of the subwidgets. | ||
| 49 | # [Hint] We set the label.width subwidget option of the Controls to | ||
| 50 | # be 16 so that their labels appear to be aligned. | ||
| 51 | # | ||
| 52 | a = Tix.Control(top, label='Number of Engines: ', integer=1, | ||
| 53 | variable=demo_num_engines, min=1, max=4, | ||
| 54 | options='entry.width 10 label.width 20 label.anchor e') | ||
| 55 | |||
| 56 | b = Tix.Control(top, label='Thrust: ', integer=0, | ||
| 57 | min='10000.0', max='60000.0', step=500, | ||
| 58 | variable=demo_thrust, | ||
| 59 | options='entry.width 10 label.width 20 label.anchor e') | ||
| 60 | |||
| 61 | c = Tix.Control(top, label='Engine Maker: ', value='P&W', | ||
| 62 | variable=demo_maker, | ||
| 63 | options='entry.width 10 label.width 20 label.anchor e') | ||
| 64 | |||
| 65 | # We can't define these in the init because the widget 'c' doesn't | ||
| 66 | # exist yet and we need to reference it | ||
| 67 | c['incrcmd'] = lambda w=c: adjust_maker(w, 1) | ||
| 68 | c['decrcmd'] = lambda w=c: adjust_maker(w, -1) | ||
| 69 | c['validatecmd'] = lambda w=c: validate_maker(w) | ||
| 70 | |||
| 71 | a.pack(side=Tix.TOP, anchor=Tix.W) | ||
| 72 | b.pack(side=Tix.TOP, anchor=Tix.W) | ||
| 73 | c.pack(side=Tix.TOP, anchor=Tix.W) | ||
| 74 | |||
| 75 | box = Tix.ButtonBox(w, orientation=Tix.HORIZONTAL) | ||
| 76 | box.add('ok', text='Ok', underline=0, width=6, | ||
| 77 | command=self.okcmd) | ||
| 78 | box.add('cancel', text='Cancel', underline=0, width=6, | ||
| 79 | command=self.quitcmd) | ||
| 80 | box.pack(side=Tix.BOTTOM, fill=Tix.X) | ||
| 81 | top.pack(side=Tix.TOP, fill=Tix.BOTH, expand=1) | ||
| 82 | |||
| 83 | def okcmd (self): | ||
| 84 | # tixDemo:Status "Selected %d of %s engines each of thrust %d", (demo_num_engines.get(), demo_maker.get(), demo_thrust.get()) | ||
| 85 | self.quitcmd() | ||
| 86 | |||
| 87 | def quitcmd (self): | ||
| 88 | self.exit = 0 | ||
| 89 | |||
| 90 | def mainloop(self): | ||
| 91 | while self.exit < 0: | ||
| 92 | self.root.tk.dooneevent(TCL_ALL_EVENTS) | ||
| 93 | |||
| 94 | def destroy (self): | ||
| 95 | self.root.destroy() | ||
| 96 | |||
| 97 | maker_list = ['P&W', 'GE', 'Rolls Royce'] | ||
| 98 | |||
| 99 | def adjust_maker(w, inc): | ||
| 100 | i = maker_list.index(demo_maker.get()) | ||
| 101 | i = i + inc | ||
| 102 | if i >= len(maker_list): | ||
| 103 | i = 0 | ||
| 104 | elif i < 0: | ||
| 105 | i = len(maker_list) - 1 | ||
| 106 | |||
| 107 | # In Tcl/Tix we should return the string maker_list[i]. We can't | ||
| 108 | # do that in Tkinter so we set the global variable. (This works). | ||
| 109 | demo_maker.set(maker_list[i]) | ||
| 110 | |||
| 111 | def validate_maker(w): | ||
| 112 | try: | ||
| 113 | i = maker_list.index(demo_maker.get()) | ||
| 114 | except ValueError: | ||
| 115 | # Works here though. Why ? Beats me. | ||
| 116 | return maker_list[0] | ||
| 117 | # Works here though. Why ? Beats me. | ||
| 118 | return maker_list[i] | ||
| 119 | |||
| 120 | if __name__ == '__main__': | ||
| 121 | root = Tix.Tk() | ||
| 122 | RunSample(root) | ||
| @@ -0,0 +1,131 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: DirList.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program using tixwish. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixDirList widget -- you can | ||
| 14 | # use it for the user to select a directory. For example, an installation | ||
| 15 | # program can use the tixDirList widget to ask the user to select the | ||
| 16 | # installation directory for an application. | ||
| 17 | # | ||
| 18 | |||
| 19 | import Tix, os, copy | ||
| 20 | from Tkconstants import * | ||
| 21 | |||
| 22 | TCL_ALL_EVENTS = 0 | ||
| 23 | |||
| 24 | def RunSample (root): | ||
| 25 | dirlist = DemoDirList(root) | ||
| 26 | dirlist.mainloop() | ||
| 27 | dirlist.destroy() | ||
| 28 | |||
| 29 | class DemoDirList: | ||
| 30 | def __init__(self, w): | ||
| 31 | self.root = w | ||
| 32 | self.exit = -1 | ||
| 33 | |||
| 34 | z = w.winfo_toplevel() | ||
| 35 | z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd()) | ||
| 36 | |||
| 37 | # Create the tixDirList and the tixLabelEntry widgets on the on the top | ||
| 38 | # of the dialog box | ||
| 39 | |||
| 40 | # bg = root.tk.eval('tix option get bg') | ||
| 41 | # adding bg=bg crashes Windows pythonw tk8.3.3 Python 2.1.0 | ||
| 42 | |||
| 43 | top = Tix.Frame( w, relief=RAISED, bd=1) | ||
| 44 | |||
| 45 | # Create the DirList widget. By default it will show the current | ||
| 46 | # directory | ||
| 47 | # | ||
| 48 | # | ||
| 49 | top.dir = Tix.DirList(top) | ||
| 50 | top.dir.hlist['width'] = 40 | ||
| 51 | |||
| 52 | # When the user presses the ".." button, the selected directory | ||
| 53 | # is "transferred" into the entry widget | ||
| 54 | # | ||
| 55 | top.btn = Tix.Button(top, text = " >> ", pady = 0) | ||
| 56 | |||
| 57 | # We use a LabelEntry to hold the installation directory. The user | ||
| 58 | # can choose from the DirList widget, or he can type in the directory | ||
| 59 | # manually | ||
| 60 | # | ||
| 61 | top.ent = Tix.LabelEntry(top, label="Installation Directory:", | ||
| 62 | labelside = 'top', | ||
| 63 | options = ''' | ||
| 64 | entry.width 40 | ||
| 65 | label.anchor w | ||
| 66 | ''') | ||
| 67 | |||
| 68 | font = self.root.tk.eval('tix option get fixed_font') | ||
| 69 | # font = self.root.master.tix_option_get('fixed_font') | ||
| 70 | top.ent.entry['font'] = font | ||
| 71 | |||
| 72 | self.dlist_dir = copy.copy(os.curdir) | ||
| 73 | # This should work setting the entry's textvariable | ||
| 74 | top.ent.entry['textvariable'] = self.dlist_dir | ||
| 75 | top.btn['command'] = lambda dir=top.dir, ent=top.ent, self=self: \ | ||
| 76 | self.copy_name(dir,ent) | ||
| 77 | |||
| 78 | # top.ent.entry.insert(0,'tix'+repr(self)) | ||
| 79 | top.ent.entry.bind('<Return>', lambda self=self: self.okcmd () ) | ||
| 80 | |||
| 81 | top.pack( expand='yes', fill='both', side=TOP) | ||
| 82 | top.dir.pack( expand=1, fill=BOTH, padx=4, pady=4, side=LEFT) | ||
| 83 | top.btn.pack( anchor='s', padx=4, pady=4, side=LEFT) | ||
| 84 | top.ent.pack( expand=1, fill=X, anchor='s', padx=4, pady=4, side=LEFT) | ||
| 85 | |||
| 86 | # Use a ButtonBox to hold the buttons. | ||
| 87 | # | ||
| 88 | box = Tix.ButtonBox (w, orientation='horizontal') | ||
| 89 | box.add ('ok', text='Ok', underline=0, width=6, | ||
| 90 | command = lambda self=self: self.okcmd () ) | ||
| 91 | box.add ('cancel', text='Cancel', underline=0, width=6, | ||
| 92 | command = lambda self=self: self.quitcmd () ) | ||
| 93 | |||
| 94 | box.pack( anchor='s', fill='x', side=BOTTOM) | ||
| 95 | |||
| 96 | def copy_name (self, dir, ent): | ||
| 97 | # This should work as it is the entry's textvariable | ||
| 98 | self.dlist_dir = dir.cget('value') | ||
| 99 | # but it isn't so I'll do it manually | ||
| 100 | ent.entry.delete(0,'end') | ||
| 101 | ent.entry.insert(0, self.dlist_dir) | ||
| 102 | |||
| 103 | def okcmd (self): | ||
| 104 | # tixDemo:Status "You have selected the directory" + self.dlist_dir | ||
| 105 | self.quitcmd() | ||
| 106 | |||
| 107 | def quitcmd (self): | ||
| 108 | self.exit = 0 | ||
| 109 | |||
| 110 | def mainloop(self): | ||
| 111 | while self.exit < 0: | ||
| 112 | self.root.tk.dooneevent(TCL_ALL_EVENTS) | ||
| 113 | |||
| 114 | def destroy (self): | ||
| 115 | self.root.destroy() | ||
| 116 | |||
| 117 | # This "if" statement makes it possible to run this script file inside or | ||
| 118 | # outside of the main demo program "tixwidgets.py". | ||
| 119 | # | ||
| 120 | if __name__== '__main__' : | ||
| 121 | import tkMessageBox, traceback | ||
| 122 | |||
| 123 | try: | ||
| 124 | root=Tix.Tk() | ||
| 125 | RunSample(root) | ||
| 126 | except: | ||
| 127 | t, v, tb = sys.exc_info() | ||
| 128 | text = "Error running the demo script:\n" | ||
| 129 | for line in traceback.format_exception(t,v,tb): | ||
| 130 | text = text + line + '\n' | ||
| 131 | d = tkMessageBox.showerror ( 'Tix Demo Error', text) | ||
| @@ -0,0 +1,117 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: DirTree.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program using tixwish. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixDirTree widget -- you can | ||
| 14 | # use it for the user to select a directory. For example, an installation | ||
| 15 | # program can use the tixDirTree widget to ask the user to select the | ||
| 16 | # installation directory for an application. | ||
| 17 | # | ||
| 18 | |||
| 19 | import Tix, os, copy | ||
| 20 | from Tkconstants import * | ||
| 21 | |||
| 22 | TCL_ALL_EVENTS = 0 | ||
| 23 | |||
| 24 | def RunSample (root): | ||
| 25 | dirtree = DemoDirTree(root) | ||
| 26 | dirtree.mainloop() | ||
| 27 | dirtree.destroy() | ||
| 28 | |||
| 29 | class DemoDirTree: | ||
| 30 | def __init__(self, w): | ||
| 31 | self.root = w | ||
| 32 | self.exit = -1 | ||
| 33 | |||
| 34 | z = w.winfo_toplevel() | ||
| 35 | z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd()) | ||
| 36 | |||
| 37 | # Create the tixDirTree and the tixLabelEntry widgets on the on the top | ||
| 38 | # of the dialog box | ||
| 39 | |||
| 40 | # bg = root.tk.eval('tix option get bg') | ||
| 41 | # adding bg=bg crashes Windows pythonw tk8.3.3 Python 2.1.0 | ||
| 42 | |||
| 43 | top = Tix.Frame( w, relief=RAISED, bd=1) | ||
| 44 | |||
| 45 | # Create the DirTree widget. By default it will show the current | ||
| 46 | # directory | ||
| 47 | # | ||
| 48 | # | ||
| 49 | top.dir = Tix.DirTree(top) | ||
| 50 | top.dir.hlist['width'] = 40 | ||
| 51 | |||
| 52 | # When the user presses the ".." button, the selected directory | ||
| 53 | # is "transferred" into the entry widget | ||
| 54 | # | ||
| 55 | top.btn = Tix.Button(top, text = " >> ", pady = 0) | ||
| 56 | |||
| 57 | # We use a LabelEntry to hold the installation directory. The user | ||
| 58 | # can choose from the DirTree widget, or he can type in the directory | ||
| 59 | # manually | ||
| 60 | # | ||
| 61 | top.ent = Tix.LabelEntry(top, label="Installation Directory:", | ||
| 62 | labelside = 'top', | ||
| 63 | options = ''' | ||
| 64 | entry.width 40 | ||
| 65 | label.anchor w | ||
| 66 | ''') | ||
| 67 | |||
| 68 | self.dlist_dir = copy.copy(os.curdir) | ||
| 69 | top.ent.entry['textvariable'] = self.dlist_dir | ||
| 70 | top.btn['command'] = lambda dir=top.dir, ent=top.ent, self=self: \ | ||
| 71 | self.copy_name(dir,ent) | ||
| 72 | |||
| 73 | top.ent.entry.bind('<Return>', lambda self=self: self.okcmd () ) | ||
| 74 | |||
| 75 | top.pack( expand='yes', fill='both', side=TOP) | ||
| 76 | top.dir.pack( expand=1, fill=BOTH, padx=4, pady=4, side=LEFT) | ||
| 77 | top.btn.pack( anchor='s', padx=4, pady=4, side=LEFT) | ||
| 78 | top.ent.pack( expand=1, fill=X, anchor='s', padx=4, pady=4, side=LEFT) | ||
| 79 | |||
| 80 | # Use a ButtonBox to hold the buttons. | ||
| 81 | # | ||
| 82 | box = Tix.ButtonBox (w, orientation='horizontal') | ||
| 83 | box.add ('ok', text='Ok', underline=0, width=6, | ||
| 84 | command = lambda self=self: self.okcmd () ) | ||
| 85 | box.add ('cancel', text='Cancel', underline=0, width=6, | ||
| 86 | command = lambda self=self: self.quitcmd () ) | ||
| 87 | |||
| 88 | box.pack( anchor='s', fill='x', side=BOTTOM) | ||
| 89 | |||
| 90 | def copy_name (self, dir, ent): | ||
| 91 | # This should work as it is the entry's textvariable | ||
| 92 | self.dlist_dir = dir.cget('value') | ||
| 93 | # but it isn't so I'll do it manually | ||
| 94 | ent.entry.delete(0,'end') | ||
| 95 | ent.entry.insert(0, self.dlist_dir) | ||
| 96 | |||
| 97 | def okcmd (self): | ||
| 98 | # tixDemo:Status "You have selected the directory" + self.dlist_dir | ||
| 99 | self.quitcmd() | ||
| 100 | |||
| 101 | def quitcmd (self): | ||
| 102 | # tixDemo:Status "You have selected the directory" + self.dlist_dir | ||
| 103 | self.exit = 0 | ||
| 104 | |||
| 105 | def mainloop(self): | ||
| 106 | while self.exit < 0: | ||
| 107 | self.root.tk.dooneevent(TCL_ALL_EVENTS) | ||
| 108 | |||
| 109 | def destroy (self): | ||
| 110 | self.root.destroy() | ||
| 111 | |||
| 112 | # This "if" statement makes it possible to run this script file inside or | ||
| 113 | # outside of the main demo program "tixwidgets.py". | ||
| 114 | # | ||
| 115 | if __name__== '__main__' : | ||
| 116 | root=Tix.Tk() | ||
| 117 | RunSample(root) | ||
| @@ -0,0 +1,119 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: NoteBook.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixNoteBook widget, which allows | ||
| 14 | # you to lay out your interface using a "notebook" metaphore | ||
| 15 | # | ||
| 16 | import Tix | ||
| 17 | |||
| 18 | def RunSample(w): | ||
| 19 | global root | ||
| 20 | root = w | ||
| 21 | |||
| 22 | # We use these options to set the sizes of the subwidgets inside the | ||
| 23 | # notebook, so that they are well-aligned on the screen. | ||
| 24 | prefix = Tix.OptionName(w) | ||
| 25 | if prefix: | ||
| 26 | prefix = '*'+prefix | ||
| 27 | else: | ||
| 28 | prefix = '' | ||
| 29 | w.option_add(prefix+'*TixControl*entry.width', 10) | ||
| 30 | w.option_add(prefix+'*TixControl*label.width', 18) | ||
| 31 | w.option_add(prefix+'*TixControl*label.anchor', Tix.E) | ||
| 32 | w.option_add(prefix+'*TixNoteBook*tagPadX', 8) | ||
| 33 | |||
| 34 | # Create the notebook widget and set its backpagecolor to gray. | ||
| 35 | # Note that the -backpagecolor option belongs to the "nbframe" | ||
| 36 | # subwidget. | ||
| 37 | nb = Tix.NoteBook(w, name='nb', ipadx=6, ipady=6) | ||
| 38 | nb['bg'] = 'gray' | ||
| 39 | nb.nbframe['backpagecolor'] = 'gray' | ||
| 40 | |||
| 41 | # Create the two tabs on the notebook. The -underline option | ||
| 42 | # puts a underline on the first character of the labels of the tabs. | ||
| 43 | # Keyboard accelerators will be defined automatically according | ||
| 44 | # to the underlined character. | ||
| 45 | nb.add('hard_disk', label="Hard Disk", underline=0) | ||
| 46 | nb.add('network', label="Network", underline=0) | ||
| 47 | |||
| 48 | nb.pack(expand=1, fill=Tix.BOTH, padx=5, pady=5 ,side=Tix.TOP) | ||
| 49 | |||
| 50 | #---------------------------------------- | ||
| 51 | # Create the first page | ||
| 52 | #---------------------------------------- | ||
| 53 | # Create two frames: one for the common buttons, one for the | ||
| 54 | # other widgets | ||
| 55 | # | ||
| 56 | tab=nb.hard_disk | ||
| 57 | f = Tix.Frame(tab) | ||
| 58 | common = Tix.Frame(tab) | ||
| 59 | |||
| 60 | f.pack(side=Tix.LEFT, padx=2, pady=2, fill=Tix.BOTH, expand=1) | ||
| 61 | common.pack(side=Tix.RIGHT, padx=2, fill=Tix.Y) | ||
| 62 | |||
| 63 | a = Tix.Control(f, value=12, label='Access time: ') | ||
| 64 | w = Tix.Control(f, value=400, label='Write Throughput: ') | ||
| 65 | r = Tix.Control(f, value=400, label='Read Throughput: ') | ||
| 66 | c = Tix.Control(f, value=1021, label='Capacity: ') | ||
| 67 | |||
| 68 | a.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 69 | w.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 70 | r.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 71 | c.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 72 | |||
| 73 | # Create the common buttons | ||
| 74 | createCommonButtons(common) | ||
| 75 | |||
| 76 | #---------------------------------------- | ||
| 77 | # Create the second page | ||
| 78 | #---------------------------------------- | ||
| 79 | |||
| 80 | tab = nb.network | ||
| 81 | |||
| 82 | f = Tix.Frame(tab) | ||
| 83 | common = Tix.Frame(tab) | ||
| 84 | |||
| 85 | f.pack(side=Tix.LEFT, padx=2, pady=2, fill=Tix.BOTH, expand=1) | ||
| 86 | common.pack(side=Tix.RIGHT, padx=2, fill=Tix.Y) | ||
| 87 | |||
| 88 | a = Tix.Control(f, value=12, label='Access time: ') | ||
| 89 | w = Tix.Control(f, value=400, label='Write Throughput: ') | ||
| 90 | r = Tix.Control(f, value=400, label='Read Throughput: ') | ||
| 91 | c = Tix.Control(f, value=1021, label='Capacity: ') | ||
| 92 | u = Tix.Control(f, value=10, label='Users: ') | ||
| 93 | |||
| 94 | a.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 95 | w.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 96 | r.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 97 | c.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 98 | u.pack(side=Tix.TOP, padx=20, pady=2) | ||
| 99 | |||
| 100 | createCommonButtons(common) | ||
| 101 | |||
| 102 | def doDestroy(): | ||
| 103 | global root | ||
| 104 | root.destroy() | ||
| 105 | |||
| 106 | def createCommonButtons(master): | ||
| 107 | ok = Tix.Button(master, name='ok', text='OK', width=6, | ||
| 108 | command=doDestroy) | ||
| 109 | cancel = Tix.Button(master, name='cancel', | ||
| 110 | text='Cancel', width=6, | ||
| 111 | command=doDestroy) | ||
| 112 | |||
| 113 | ok.pack(side=Tix.TOP, padx=2, pady=2) | ||
| 114 | cancel.pack(side=Tix.TOP, padx=2, pady=2) | ||
| 115 | |||
| 116 | if __name__ == '__main__': | ||
| 117 | root = Tix.Tk() | ||
| 118 | RunSample(root) | ||
| 119 | root.mainloop() | ||
| @@ -0,0 +1,68 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: OptMenu.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixOptionMenu widget -- you can | ||
| 14 | # use it for the user to choose from a fixed set of options | ||
| 15 | # | ||
| 16 | import Tix | ||
| 17 | |||
| 18 | options = {'text':'Plain Text', 'post':'PostScript', 'html':'HTML', | ||
| 19 | 'tex':'LaTeX', 'rtf':'Rich Text Format'} | ||
| 20 | |||
| 21 | def RunSample(w): | ||
| 22 | global demo_opt_from, demo_opt_to | ||
| 23 | |||
| 24 | demo_opt_from = Tix.StringVar() | ||
| 25 | demo_opt_to = Tix.StringVar() | ||
| 26 | |||
| 27 | top = Tix.Frame(w, bd=1, relief=Tix.RAISED) | ||
| 28 | |||
| 29 | from_file = Tix.OptionMenu(top, label="From File Format : ", | ||
| 30 | variable=demo_opt_from, | ||
| 31 | options = 'label.width 19 label.anchor e menubutton.width 15') | ||
| 32 | |||
| 33 | to_file = Tix.OptionMenu(top, label="To File Format : ", | ||
| 34 | variable=demo_opt_to, | ||
| 35 | options='label.width 19 label.anchor e menubutton.width 15') | ||
| 36 | |||
| 37 | # Add the available options to the two OptionMenu widgets | ||
| 38 | # | ||
| 39 | # [Hint] You have to add the options first before you set the | ||
| 40 | # global variables "demo_opt_from" and "demo_opt_to". Otherwise | ||
| 41 | # the OptionMenu widget will complain about "unknown options"! | ||
| 42 | # | ||
| 43 | for opt in options.keys(): | ||
| 44 | from_file.add_command(opt, label=options[opt]) | ||
| 45 | to_file.add_command(opt, label=options[opt]) | ||
| 46 | |||
| 47 | demo_opt_from.set('html') | ||
| 48 | demo_opt_to.set('post') | ||
| 49 | |||
| 50 | from_file.pack(side=Tix.TOP, anchor=Tix.W, pady=3, padx=6) | ||
| 51 | to_file.pack(side=Tix.TOP, anchor=Tix.W, pady=3, padx=6) | ||
| 52 | |||
| 53 | box = Tix.ButtonBox(w, orientation=Tix.HORIZONTAL) | ||
| 54 | box.add('ok', text='Ok', underline=0, width=6, | ||
| 55 | command=lambda w=w: ok_command(w)) | ||
| 56 | box.add('cancel', text='Cancel', underline=0, width=6, | ||
| 57 | command=lambda w=w: w.destroy()) | ||
| 58 | box.pack(side=Tix.BOTTOM, fill=Tix.X) | ||
| 59 | top.pack(side=Tix.TOP, fill=Tix.BOTH, expand=1) | ||
| 60 | |||
| 61 | def ok_command(w): | ||
| 62 | # tixDemo:Status "Convert file from %s to %s" % ( demo_opt_from.get(), demo_opt_to.get()) | ||
| 63 | w.destroy() | ||
| 64 | |||
| 65 | if __name__ == '__main__': | ||
| 66 | root = Tix.Tk() | ||
| 67 | RunSample(root) | ||
| 68 | root.mainloop() | ||
| @@ -0,0 +1,98 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: PanedWin.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixPanedWindow widget. This program | ||
| 14 | # is a dummy news reader: the user can adjust the sizes of the list | ||
| 15 | # of artical names and the size of the text widget that shows the body | ||
| 16 | # of the article. | ||
| 17 | |||
| 18 | import Tix | ||
| 19 | |||
| 20 | TCL_ALL_EVENTS = 0 | ||
| 21 | |||
| 22 | def RunSample (root): | ||
| 23 | panedwin = DemoPanedwin(root) | ||
| 24 | panedwin.mainloop() | ||
| 25 | panedwin.destroy() | ||
| 26 | |||
| 27 | class DemoPanedwin: | ||
| 28 | def __init__(self, w): | ||
| 29 | self.root = w | ||
| 30 | self.exit = -1 | ||
| 31 | |||
| 32 | z = w.winfo_toplevel() | ||
| 33 | z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd()) | ||
| 34 | |||
| 35 | group = Tix.LabelEntry(w, label='Newsgroup:', options='entry.width 25') | ||
| 36 | group.entry.insert(0,'comp.lang.python') | ||
| 37 | pane = Tix.PanedWindow(w, orientation='vertical') | ||
| 38 | |||
| 39 | p1 = pane.add('list', min=70, size=100) | ||
| 40 | p2 = pane.add('text', min=70) | ||
| 41 | list = Tix.ScrolledListBox(p1) | ||
| 42 | list.listbox['width'] = 80 | ||
| 43 | list.listbox['height'] = 5 | ||
| 44 | text = Tix.ScrolledText(p2) | ||
| 45 | text.text['width'] = 80 | ||
| 46 | text.text['height'] = 20 | ||
| 47 | |||
| 48 | list.listbox.insert(Tix.END, " 12324 Re: Tkinter is good for your health") | ||
| 49 | list.listbox.insert(Tix.END, "+ 12325 Re: Tkinter is good for your health") | ||
| 50 | list.listbox.insert(Tix.END, "+ 12326 Re: Tix is even better for your health (Was: Tkinter is good...)") | ||
| 51 | list.listbox.insert(Tix.END, " 12327 Re: Tix is even better for your health (Was: Tkinter is good...)") | ||
| 52 | list.listbox.insert(Tix.END, "+ 12328 Re: Tix is even better for your health (Was: Tkinter is good...)") | ||
| 53 | list.listbox.insert(Tix.END, " 12329 Re: Tix is even better for your health (Was: Tkinter is good...)") | ||
| 54 | list.listbox.insert(Tix.END, "+ 12330 Re: Tix is even better for your health (Was: Tkinter is good...)") | ||
| 55 | |||
| 56 | text.text['bg'] = list.listbox['bg'] | ||
| 57 | text.text['wrap'] = 'none' | ||
| 58 | text.text.insert(Tix.END, """ | ||
| 59 | Mon, 19 Jun 1995 11:39:52 comp.lang.python Thread 34 of 220 | ||
| 60 | Lines 353 A new way to put text and bitmaps together iNo responses | ||
| 61 | ioi@blue.seas.upenn.edu Ioi K. Lam at University of Pennsylvania | ||
| 62 | |||
| 63 | Hi, | ||
| 64 | |||
| 65 | I have implemented a new image type called "compound". It allows you | ||
| 66 | to glue together a bunch of bitmaps, images and text strings together | ||
| 67 | to form a bigger image. Then you can use this image with widgets that | ||
| 68 | support the -image option. For example, you can display a text string string | ||
| 69 | together with a bitmap, at the same time, inside a TK button widget. | ||
| 70 | """) | ||
| 71 | text.text['state'] = 'disabled' | ||
| 72 | |||
| 73 | list.pack(expand=1, fill=Tix.BOTH, padx=4, pady=6) | ||
| 74 | text.pack(expand=1, fill=Tix.BOTH, padx=4, pady=6) | ||
| 75 | |||
| 76 | group.pack(side=Tix.TOP, padx=3, pady=3, fill=Tix.BOTH) | ||
| 77 | pane.pack(side=Tix.TOP, padx=3, pady=3, fill=Tix.BOTH, expand=1) | ||
| 78 | |||
| 79 | box = Tix.ButtonBox(w, orientation=Tix.HORIZONTAL) | ||
| 80 | box.add('ok', text='Ok', underline=0, width=6, | ||
| 81 | command=self.quitcmd) | ||
| 82 | box.add('cancel', text='Cancel', underline=0, width=6, | ||
| 83 | command=self.quitcmd) | ||
| 84 | box.pack(side=Tix.BOTTOM, fill=Tix.X) | ||
| 85 | |||
| 86 | def quitcmd (self): | ||
| 87 | self.exit = 0 | ||
| 88 | |||
| 89 | def mainloop(self): | ||
| 90 | while self.exit < 0: | ||
| 91 | self.root.tk.dooneevent(TCL_ALL_EVENTS) | ||
| 92 | |||
| 93 | def destroy (self): | ||
| 94 | self.root.destroy() | ||
| 95 | |||
| 96 | if __name__ == '__main__': | ||
| 97 | root = Tix.Tk() | ||
| 98 | RunSample(root) | ||
| @@ -0,0 +1,57 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: PopMenu.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program using tixwish. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixPopupMenu widget. | ||
| 14 | # | ||
| 15 | import Tix | ||
| 16 | |||
| 17 | def RunSample(w): | ||
| 18 | # We create the frame and the button, then we'll bind the PopupMenu | ||
| 19 | # to both widgets. The result is, when you press the right mouse | ||
| 20 | # button over $w.top or $w.top.but, the PopupMenu will come up. | ||
| 21 | # | ||
| 22 | top = Tix.Frame(w, relief=Tix.RAISED, bd=1) | ||
| 23 | but = Tix.Button(top, text='Press the right mouse button over this button or its surrounding area') | ||
| 24 | but.pack(expand=1, fill=Tix.BOTH, padx=50, pady=50) | ||
| 25 | |||
| 26 | p = Tix.PopupMenu(top, title='Popup Test') | ||
| 27 | p.bind_widget(top) | ||
| 28 | p.bind_widget(but) | ||
| 29 | |||
| 30 | # Set the entries inside the PopupMenu widget. | ||
| 31 | # [Hint] You have to manipulate the "menu" subwidget. | ||
| 32 | # $w.top.p itself is NOT a menu widget. | ||
| 33 | # [Hint] Watch carefully how the sub-menu is created | ||
| 34 | # | ||
| 35 | p.menu.add_command(label='Desktop', underline=0) | ||
| 36 | p.menu.add_command(label='Select', underline=0) | ||
| 37 | p.menu.add_command(label='Find', underline=0) | ||
| 38 | p.menu.add_command(label='System', underline=1) | ||
| 39 | p.menu.add_command(label='Help', underline=0) | ||
| 40 | m1 = Tix.Menu(p.menu) | ||
| 41 | m1.add_command(label='Hello') | ||
| 42 | p.menu.add_cascade(label='More', menu=m1) | ||
| 43 | |||
| 44 | but.pack(side=Tix.TOP, padx=40, pady=50) | ||
| 45 | |||
| 46 | box = Tix.ButtonBox(w, orientation=Tix.HORIZONTAL) | ||
| 47 | box.add('ok', text='Ok', underline=0, width=6, | ||
| 48 | command=lambda w=w: w.destroy()) | ||
| 49 | box.add('cancel', text='Cancel', underline=0, width=6, | ||
| 50 | command=lambda w=w: w.destroy()) | ||
| 51 | box.pack(side=Tix.BOTTOM, fill=Tix.X) | ||
| 52 | top.pack(side=Tix.TOP, fill=Tix.BOTH, expand=1) | ||
| 53 | |||
| 54 | if __name__ == '__main__': | ||
| 55 | root = Tix.Tk() | ||
| 56 | RunSample(root) | ||
| 57 | root.mainloop() | ||
| @@ -0,0 +1,131 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: SHList1.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program using tixwish. | ||
| 12 | |||
| 13 | # This file demonstrates the use of the tixScrolledHList widget. | ||
| 14 | # | ||
| 15 | |||
| 16 | import Tix | ||
| 17 | |||
| 18 | TCL_ALL_EVENTS = 0 | ||
| 19 | |||
| 20 | def RunSample (root): | ||
| 21 | shlist = DemoSHList(root) | ||
| 22 | shlist.mainloop() | ||
| 23 | shlist.destroy() | ||
| 24 | |||
| 25 | class DemoSHList: | ||
| 26 | def __init__(self, w): | ||
| 27 | self.root = w | ||
| 28 | self.exit = -1 | ||
| 29 | |||
| 30 | z = w.winfo_toplevel() | ||
| 31 | z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd()) | ||
| 32 | |||
| 33 | # We create the frame and the ScrolledHList widget | ||
| 34 | # at the top of the dialog box | ||
| 35 | # | ||
| 36 | top = Tix.Frame( w, relief=Tix.RAISED, bd=1) | ||
| 37 | |||
| 38 | # Put a simple hierachy into the HList (two levels). Use colors and | ||
| 39 | # separator widgets (frames) to make the list look fancy | ||
| 40 | # | ||
| 41 | top.a = Tix.ScrolledHList(top) | ||
| 42 | top.a.pack( expand=1, fill=Tix.BOTH, padx=10, pady=10, side=Tix.TOP) | ||
| 43 | |||
| 44 | # This is our little relational database | ||
| 45 | # | ||
| 46 | bosses = [ | ||
| 47 | ('jeff', 'Jeff Waxman'), | ||
| 48 | ('john', 'John Lee'), | ||
| 49 | ('peter', 'Peter Kenson') | ||
| 50 | ] | ||
| 51 | |||
| 52 | employees = [ | ||
| 53 | ('alex', 'john', 'Alex Kellman'), | ||
| 54 | ('alan', 'john', 'Alan Adams'), | ||
| 55 | ('andy', 'peter', 'Andreas Crawford'), | ||
| 56 | ('doug', 'jeff', 'Douglas Bloom'), | ||
| 57 | ('jon', 'peter', 'Jon Baraki'), | ||
| 58 | ('chris', 'jeff', 'Chris Geoffrey'), | ||
| 59 | ('chuck', 'jeff', 'Chuck McLean') | ||
| 60 | ] | ||
| 61 | |||
| 62 | hlist=top.a.hlist | ||
| 63 | |||
| 64 | # Let configure the appearance of the HList subwidget | ||
| 65 | # | ||
| 66 | hlist.config( separator='.', width=25, drawbranch=0, indent=10) | ||
| 67 | |||
| 68 | count=0 | ||
| 69 | for boss,name in bosses : | ||
| 70 | if count : | ||
| 71 | f=Tix.Frame(hlist, name='sep%d' % count, height=2, width=150, | ||
| 72 | bd=2, relief=Tix.SUNKEN ) | ||
| 73 | |||
| 74 | hlist.add_child( itemtype=Tix.WINDOW, | ||
| 75 | window=f, state=Tix.DISABLED ) | ||
| 76 | |||
| 77 | hlist.add(boss, itemtype=Tix.TEXT, text=name) | ||
| 78 | count = count+1 | ||
| 79 | |||
| 80 | |||
| 81 | for person,boss,name in employees : | ||
| 82 | # '.' is the separator character we chose above | ||
| 83 | # | ||
| 84 | key= boss + '.' + person | ||
| 85 | # ^^^^ ^^^^^^ | ||
| 86 | # parent entryPath / child's name | ||
| 87 | |||
| 88 | hlist.add( key, text=name ) | ||
| 89 | |||
| 90 | # [Hint] Make sure the keys (e.g. 'boss.person') you choose | ||
| 91 | # are unique names. If you cannot be sure of this (because of | ||
| 92 | # the structure of your database, e.g.) you can use the | ||
| 93 | # "add_child" command instead: | ||
| 94 | # | ||
| 95 | # hlist.addchild( boss, text=name) | ||
| 96 | # ^^^^ | ||
| 97 | # parent entryPath | ||
| 98 | |||
| 99 | |||
| 100 | # Use a ButtonBox to hold the buttons. | ||
| 101 | # | ||
| 102 | box= Tix.ButtonBox(top, orientation=Tix.HORIZONTAL ) | ||
| 103 | box.add( 'ok', text='Ok', underline=0, width=6, | ||
| 104 | command = self.okcmd) | ||
| 105 | |||
| 106 | box.add( 'cancel', text='Cancel', underline=0, width=6, | ||
| 107 | command = self.quitcmd) | ||
| 108 | |||
| 109 | box.pack( side=Tix.BOTTOM, fill=Tix.X) | ||
| 110 | top.pack( side=Tix.TOP, fill=Tix.BOTH, expand=1 ) | ||
| 111 | |||
| 112 | def okcmd (self): | ||
| 113 | self.quitcmd() | ||
| 114 | |||
| 115 | def quitcmd (self): | ||
| 116 | self.exit = 0 | ||
| 117 | |||
| 118 | def mainloop(self): | ||
| 119 | while self.exit < 0: | ||
| 120 | self.root.tk.dooneevent(TCL_ALL_EVENTS) | ||
| 121 | |||
| 122 | def destroy (self): | ||
| 123 | self.root.destroy() | ||
| 124 | |||
| 125 | |||
| 126 | # This "if" statement makes it possible to run this script file inside or | ||
| 127 | # outside of the main demo program "tixwidgets.py". | ||
| 128 | # | ||
| 129 | if __name__== '__main__' : | ||
| 130 | root=Tix.Tk() | ||
| 131 | RunSample(root) | ||
| @@ -0,0 +1,168 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: SHList2.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidget": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program using tixwish. | ||
| 12 | |||
| 13 | # This file demonstrates how to use multiple columns and multiple styles | ||
| 14 | # in the tixHList widget | ||
| 15 | # | ||
| 16 | # In a tixHList widget, you can have one ore more columns. | ||
| 17 | # | ||
| 18 | |||
| 19 | import Tix | ||
| 20 | |||
| 21 | TCL_ALL_EVENTS = 0 | ||
| 22 | |||
| 23 | def RunSample (root): | ||
| 24 | shlist = DemoSHList(root) | ||
| 25 | shlist.mainloop() | ||
| 26 | shlist.destroy() | ||
| 27 | |||
| 28 | class DemoSHList: | ||
| 29 | def __init__(self, w): | ||
| 30 | self.root = w | ||
| 31 | self.exit = -1 | ||
| 32 | |||
| 33 | z = w.winfo_toplevel() | ||
| 34 | z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd()) | ||
| 35 | |||
| 36 | # We create the frame and the ScrolledHList widget | ||
| 37 | # at the top of the dialog box | ||
| 38 | # | ||
| 39 | top = Tix.Frame( w, relief=Tix.RAISED, bd=1) | ||
| 40 | |||
| 41 | # Put a simple hierachy into the HList (two levels). Use colors and | ||
| 42 | # separator widgets (frames) to make the list look fancy | ||
| 43 | # | ||
| 44 | top.a = Tix.ScrolledHList(top, options='hlist.columns 3 hlist.header 1' ) | ||
| 45 | top.a.pack( expand=1, fill=Tix.BOTH, padx=10, pady=10, side=Tix.TOP) | ||
| 46 | |||
| 47 | hlist=top.a.hlist | ||
| 48 | |||
| 49 | # Create the title for the HList widget | ||
| 50 | # >> Notice that we have set the hlist.header subwidget option to true | ||
| 51 | # so that the header is displayed | ||
| 52 | # | ||
| 53 | |||
| 54 | boldfont=hlist.tk.call('tix','option','get','bold_font') | ||
| 55 | |||
| 56 | # First some styles for the headers | ||
| 57 | style={} | ||
| 58 | style['header'] = Tix.DisplayStyle(Tix.TEXT, refwindow=hlist, | ||
| 59 | anchor=Tix.CENTER, padx=8, pady=2, font = boldfont ) | ||
| 60 | |||
| 61 | hlist.header_create(0, itemtype=Tix.TEXT, text='Name', | ||
| 62 | style=style['header']) | ||
| 63 | hlist.header_create(1, itemtype=Tix.TEXT, text='Position', | ||
| 64 | style=style['header']) | ||
| 65 | |||
| 66 | # Notice that we use 3 columns in the hlist widget. This way when the user | ||
| 67 | # expands the windows wide, the right side of the header doesn't look | ||
| 68 | # chopped off. The following line ensures that the 3 column header is | ||
| 69 | # not shown unless the hlist window is wider than its contents. | ||
| 70 | # | ||
| 71 | hlist.column_width(2,0) | ||
| 72 | |||
| 73 | # This is our little relational database | ||
| 74 | # | ||
| 75 | boss = ('doe', 'John Doe', 'Director') | ||
| 76 | |||
| 77 | managers = [ | ||
| 78 | ('jeff', 'Jeff Waxman', 'Manager'), | ||
| 79 | ('john', 'John Lee', 'Manager'), | ||
| 80 | ('peter', 'Peter Kenson', 'Manager') | ||
| 81 | ] | ||
| 82 | |||
| 83 | employees = [ | ||
| 84 | ('alex', 'john', 'Alex Kellman', 'Clerk'), | ||
| 85 | ('alan', 'john', 'Alan Adams', 'Clerk'), | ||
| 86 | ('andy', 'peter', 'Andreas Crawford', 'Salesman'), | ||
| 87 | ('doug', 'jeff', 'Douglas Bloom', 'Clerk'), | ||
| 88 | ('jon', 'peter', 'Jon Baraki', 'Salesman'), | ||
| 89 | ('chris', 'jeff', 'Chris Geoffrey', 'Clerk'), | ||
| 90 | ('chuck', 'jeff', 'Chuck McLean', 'Cleaner') | ||
| 91 | ] | ||
| 92 | |||
| 93 | style['mgr_name'] = Tix.DisplayStyle(Tix.TEXT, refwindow=hlist) | ||
| 94 | |||
| 95 | style['mgr_posn'] = Tix.DisplayStyle(Tix.TEXT, padx=8, refwindow=hlist) | ||
| 96 | |||
| 97 | style['empl_name'] = Tix.DisplayStyle(Tix.TEXT, refwindow=hlist) | ||
| 98 | |||
| 99 | style['empl_posn'] = Tix.DisplayStyle(Tix.TEXT, padx=8, refwindow=hlist) | ||
| 100 | |||
| 101 | # Let configure the appearance of the HList subwidget | ||
| 102 | # | ||
| 103 | hlist.config(separator='.', width=25, drawbranch=0, indent=10) | ||
| 104 | hlist.column_width(0, chars=20) | ||
| 105 | |||
| 106 | # Create the boss | ||
| 107 | # | ||
| 108 | hlist.add ('.', itemtype=Tix.TEXT, text=boss[1], | ||
| 109 | style=style['mgr_name']) | ||
| 110 | hlist.item_create('.', 1, itemtype=Tix.TEXT, text=boss[2], | ||
| 111 | style=style['mgr_posn']) | ||
| 112 | |||
| 113 | # Create the managers | ||
| 114 | # | ||
| 115 | |||
| 116 | for key,name,posn in managers : | ||
| 117 | e= '.'+ key | ||
| 118 | hlist.add(e, itemtype=Tix.TEXT, text=name, | ||
| 119 | style=style['mgr_name']) | ||
| 120 | hlist.item_create(e, 1, itemtype=Tix.TEXT, text=posn, | ||
| 121 | style=style['mgr_posn']) | ||
| 122 | |||
| 123 | |||
| 124 | for key,mgr,name,posn in employees : | ||
| 125 | # "." is the separator character we chose above | ||
| 126 | |||
| 127 | entrypath = '.' + mgr + '.' + key | ||
| 128 | |||
| 129 | # ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ | ||
| 130 | # parent entryPath / child's name | ||
| 131 | |||
| 132 | hlist.add(entrypath, text=name, style=style['empl_name']) | ||
| 133 | hlist.item_create(entrypath, 1, itemtype=Tix.TEXT, | ||
| 134 | text = posn, style = style['empl_posn'] ) | ||
| 135 | |||
| 136 | |||
| 137 | # Use a ButtonBox to hold the buttons. | ||
| 138 | # | ||
| 139 | box= Tix.ButtonBox(top, orientation=Tix.HORIZONTAL ) | ||
| 140 | box.add( 'ok', text='Ok', underline=0, width=6, | ||
| 141 | command = self.okcmd ) | ||
| 142 | |||
| 143 | box.add( 'cancel', text='Cancel', underline=0, width=6, | ||
| 144 | command = self.quitcmd ) | ||
| 145 | |||
| 146 | box.pack( side=Tix.BOTTOM, fill=Tix.X) | ||
| 147 | top.pack( side=Tix.TOP, fill=Tix.BOTH, expand=1 ) | ||
| 148 | |||
| 149 | def okcmd (self): | ||
| 150 | self.quitcmd() | ||
| 151 | |||
| 152 | def quitcmd (self): | ||
| 153 | self.exit = 0 | ||
| 154 | |||
| 155 | def mainloop(self): | ||
| 156 | while self.exit < 0: | ||
| 157 | self.root.tk.dooneevent(TCL_ALL_EVENTS) | ||
| 158 | |||
| 159 | def destroy (self): | ||
| 160 | self.root.destroy() | ||
| 161 | |||
| 162 | |||
| 163 | # This "if" statement makes it possible to run this script file inside or | ||
| 164 | # outside of the main demo program "tixwidgets.py". | ||
| 165 | # | ||
| 166 | if __name__== '__main__' : | ||
| 167 | root=Tix.Tk() | ||
| 168 | RunSample(root) | ||
| @@ -0,0 +1,80 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: Tree.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # Tix Demostration Program | ||
| 6 | # | ||
| 7 | # This sample program is structured in such a way so that it can be | ||
| 8 | # executed from the Tix demo program "tixwidgets.py": it must have a | ||
| 9 | # procedure called "RunSample". It should also have the "if" statment | ||
| 10 | # at the end of this file so that it can be run as a standalone | ||
| 11 | # program. | ||
| 12 | |||
| 13 | # This file demonstrates how to use the TixTree widget to display | ||
| 14 | # dynamic hierachical data (the files in the Unix file system) | ||
| 15 | # | ||
| 16 | |||
| 17 | import Tix, os | ||
| 18 | |||
| 19 | def RunSample(w): | ||
| 20 | top = Tix.Frame(w, relief=Tix.RAISED, bd=1) | ||
| 21 | tree = Tix.Tree(top, options='separator "/"') | ||
| 22 | tree.pack(expand=1, fill=Tix.BOTH, padx=10, pady=10, side=Tix.LEFT) | ||
| 23 | tree['opencmd'] = lambda dir=None, w=tree: opendir(w, dir) | ||
| 24 | |||
| 25 | # The / directory is added in the "open" mode. The user can open it | ||
| 26 | # and then browse its subdirectories ... | ||
| 27 | adddir(tree, "/") | ||
| 28 | |||
| 29 | box = Tix.ButtonBox(w, orientation=Tix.HORIZONTAL) | ||
| 30 | box.add('ok', text='Ok', underline=0, command=w.destroy, width=6) | ||
| 31 | box.add('cancel', text='Cancel', underline=0, command=w.destroy, width=6) | ||
| 32 | box.pack(side=Tix.BOTTOM, fill=Tix.X) | ||
| 33 | top.pack(side=Tix.TOP, fill=Tix.BOTH, expand=1) | ||
| 34 | |||
| 35 | def adddir(tree, dir): | ||
| 36 | if dir == '/': | ||
| 37 | text = '/' | ||
| 38 | else: | ||
| 39 | text = os.path.basename(dir) | ||
| 40 | tree.hlist.add(dir, itemtype=Tix.IMAGETEXT, text=text, | ||
| 41 | image=tree.tk.call('tix', 'getimage', 'folder')) | ||
| 42 | try: | ||
| 43 | os.listdir(dir) | ||
| 44 | tree.setmode(dir, 'open') | ||
| 45 | except os.error: | ||
| 46 | # No read permission ? | ||
| 47 | pass | ||
| 48 | |||
| 49 | # This function is called whenever the user presses the (+) indicator or | ||
| 50 | # double clicks on a directory whose mode is "open". It loads the files | ||
| 51 | # inside that directory into the Tree widget. | ||
| 52 | # | ||
| 53 | # Note we didn't specify the closecmd option for the Tree widget, so it | ||
| 54 | # performs the default action when the user presses the (-) indicator or | ||
| 55 | # double clicks on a directory whose mode is "close": hide all of its child | ||
| 56 | # entries | ||
| 57 | def opendir(tree, dir): | ||
| 58 | entries = tree.hlist.info_children(dir) | ||
| 59 | if entries: | ||
| 60 | # We have already loaded this directory. Let's just | ||
| 61 | # show all the child entries | ||
| 62 | # | ||
| 63 | # Note: since we load the directory only once, it will not be | ||
| 64 | # refreshed if the you add or remove files from this | ||
| 65 | # directory. | ||
| 66 | # | ||
| 67 | for entry in entries: | ||
| 68 | tree.hlist.show_entry(entry) | ||
| 69 | files = os.listdir(dir) | ||
| 70 | for file in files: | ||
| 71 | if os.path.isdir(dir + '/' + file): | ||
| 72 | adddir(tree, dir + '/' + file) | ||
| 73 | else: | ||
| 74 | tree.hlist.add(dir + '/' + file, itemtype=Tix.IMAGETEXT, text=file, | ||
| 75 | image=tree.tk.call('tix', 'getimage', 'file')) | ||
| 76 | |||
| 77 | if __name__ == '__main__': | ||
| 78 | root = Tix.Tk() | ||
| 79 | RunSample(root) | ||
| 80 | root.mainloop() | ||
| @@ -0,0 +1,1003 @@ | |||
| 1 | # -*-mode: python; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | ||
| 2 | # | ||
| 3 | # $Id: tixwidgets.py 36560 2004-07-18 06:16:08Z tim_one $ | ||
| 4 | # | ||
| 5 | # tixwidgets.py -- | ||
| 6 | # | ||
| 7 | # For Tix, see http://tix.sourceforge.net | ||
| 8 | # | ||
| 9 | # This is a demo program of some of the Tix widgets available in Python. | ||
| 10 | # If you have installed Python & Tix properly, you can execute this as | ||
| 11 | # | ||
| 12 | # % python tixwidgets.py | ||
| 13 | # | ||
| 14 | |||
| 15 | import os, os.path, sys, Tix | ||
| 16 | from Tkconstants import * | ||
| 17 | import traceback, tkMessageBox | ||
| 18 | |||
| 19 | TCL_DONT_WAIT = 1<<1 | ||
| 20 | TCL_WINDOW_EVENTS = 1<<2 | ||
| 21 | TCL_FILE_EVENTS = 1<<3 | ||
| 22 | TCL_TIMER_EVENTS = 1<<4 | ||
| 23 | TCL_IDLE_EVENTS = 1<<5 | ||
| 24 | TCL_ALL_EVENTS = 0 | ||
| 25 | |||
| 26 | class Demo: | ||
| 27 | def __init__(self, top): | ||
| 28 | self.root = top | ||
| 29 | self.exit = -1 | ||
| 30 | |||
| 31 | self.dir = None # script directory | ||
| 32 | self.balloon = None # balloon widget | ||
| 33 | self.useBalloons = Tix.StringVar() | ||
| 34 | self.useBalloons.set('0') | ||
| 35 | self.statusbar = None # status bar widget | ||
| 36 | self.welmsg = None # Msg widget | ||
| 37 | self.welfont = '' # font name | ||
| 38 | self.welsize = '' # font size | ||
| 39 | |||
| 40 | progname = sys.argv[0] | ||
| 41 | dirname = os.path.dirname(progname) | ||
| 42 | if dirname and dirname != os.curdir: | ||
| 43 | self.dir = dirname | ||
| 44 | index = -1 | ||
| 45 | for i in range(len(sys.path)): | ||
| 46 | p = sys.path[i] | ||
| 47 | if p in ("", os.curdir): | ||
| 48 | index = i | ||
| 49 | if index >= 0: | ||
| 50 | sys.path[index] = dirname | ||
| 51 | else: | ||
| 52 | sys.path.insert(0, dirname) | ||
| 53 | else: | ||
| 54 | self.dir = os.getcwd() | ||
| 55 | sys.path.insert(0, self.dir+'/samples') | ||
| 56 | |||
| 57 | def MkMainMenu(self): | ||
| 58 | top = self.root | ||
| 59 | w = Tix.Frame(top, bd=2, relief=RAISED) | ||
| 60 | file = Tix.Menubutton(w, text='File', underline=0, takefocus=0) | ||
| 61 | help = Tix.Menubutton(w, text='Help', underline=0, takefocus=0) | ||
| 62 | file.pack(side=LEFT) | ||
| 63 | help.pack(side=RIGHT) | ||
| 64 | fm = Tix.Menu(file, tearoff=0) | ||
| 65 | file['menu'] = fm | ||
| 66 | hm = Tix.Menu(help, tearoff=0) | ||
| 67 | help['menu'] = hm | ||
| 68 | |||
| 69 | fm.add_command(label='Exit', underline=1, | ||
| 70 | command = lambda self=self: self.quitcmd () ) | ||
| 71 | hm.add_checkbutton(label='BalloonHelp', underline=0, command=ToggleHelp, | ||
| 72 | variable=self.useBalloons) | ||
| 73 | # The trace variable option doesn't seem to work, instead I use 'command' | ||
| 74 | #apply(w.tk.call, ('trace', 'variable', self.useBalloons, 'w', | ||
| 75 | # ToggleHelp)) | ||
| 76 | |||
| 77 | return w | ||
| 78 | |||
| 79 | def MkMainNotebook(self): | ||
| 80 | top = self.root | ||
| 81 | w = Tix.NoteBook(top, ipadx=5, ipady=5, options=""" | ||
| 82 | tagPadX 6 | ||
| 83 | tagPadY 4 | ||
| 84 | borderWidth 2 | ||
| 85 | """) | ||
| 86 | # This may be required if there is no *Background option | ||
| 87 | top['bg'] = w['bg'] | ||
| 88 | |||
| 89 | w.add('wel', label='Welcome', underline=0, | ||
| 90 | createcmd=lambda w=w, name='wel': MkWelcome(w, name)) | ||
| 91 | w.add('cho', label='Choosers', underline=0, | ||
| 92 | createcmd=lambda w=w, name='cho': MkChoosers(w, name)) | ||
| 93 | w.add('scr', label='Scrolled Widgets', underline=0, | ||
| 94 | createcmd=lambda w=w, name='scr': MkScroll(w, name)) | ||
| 95 | w.add('mgr', label='Manager Widgets', underline=0, | ||
| 96 | createcmd=lambda w=w, name='mgr': MkManager(w, name)) | ||
| 97 | w.add('dir', label='Directory List', underline=0, | ||
| 98 | createcmd=lambda w=w, name='dir': MkDirList(w, name)) | ||
| 99 | w.add('exp', label='Run Sample Programs', underline=0, | ||
| 100 | createcmd=lambda w=w, name='exp': MkSample(w, name)) | ||
| 101 | return w | ||
| 102 | |||
| 103 | def MkMainStatus(self): | ||
| 104 | global demo | ||
| 105 | top = self.root | ||
| 106 | |||
| 107 | w = Tix.Frame(top, relief=Tix.RAISED, bd=1) | ||
| 108 | demo.statusbar = Tix.Label(w, relief=Tix.SUNKEN, bd=1) | ||
| 109 | demo.statusbar.form(padx=3, pady=3, left=0, right='%70') | ||
| 110 | return w | ||
| 111 | |||
| 112 | def build(self): | ||
| 113 | root = self.root | ||
| 114 | z = root.winfo_toplevel() | ||
| 115 | z.wm_title('Tix Widget Demonstration') | ||
| 116 | if z.winfo_screenwidth() <= 800: | ||
| 117 | z.geometry('790x590+10+10') | ||
| 118 | else: | ||
| 119 | z.geometry('890x640+10+10') | ||
| 120 | demo.balloon = Tix.Balloon(root) | ||
| 121 | frame1 = self.MkMainMenu() | ||
| 122 | frame2 = self.MkMainNotebook() | ||
| 123 | frame3 = self.MkMainStatus() | ||
| 124 | frame1.pack(side=TOP, fill=X) | ||
| 125 | frame3.pack(side=BOTTOM, fill=X) | ||
| 126 | frame2.pack(side=TOP, expand=1, fill=BOTH, padx=4, pady=4) | ||
| 127 | demo.balloon['statusbar'] = demo.statusbar | ||
| 128 | z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd()) | ||
| 129 | |||
| 130 | # To show Tcl errors - uncomment this to see the listbox bug. | ||
| 131 | # Tkinter defines a Tcl tkerror procedure that in effect | ||
| 132 | # silences all background Tcl error reporting. | ||
| 133 | # root.tk.eval('if {[info commands tkerror] != ""} {rename tkerror pytkerror}') | ||
| 134 | def quitcmd (self): | ||
| 135 | """Quit our mainloop. It is up to you to call root.destroy() after.""" | ||
| 136 | self.exit = 0 | ||
| 137 | |||
| 138 | def loop(self): | ||
| 139 | """This is an explict replacement for _tkinter mainloop() | ||
| 140 | It lets you catch keyboard interrupts easier, and avoids | ||
| 141 | the 20 msec. dead sleep() which burns a constant CPU.""" | ||
| 142 | while self.exit < 0: | ||
| 143 | # There are 2 whiles here. The outer one lets you continue | ||
| 144 | # after a ^C interrupt. | ||
| 145 | try: | ||
| 146 | # This is the replacement for _tkinter mainloop() | ||
| 147 | # It blocks waiting for the next Tcl event using select. | ||
| 148 | while self.exit < 0: | ||
| 149 | self.root.tk.dooneevent(TCL_ALL_EVENTS) | ||
| 150 | except SystemExit: | ||
| 151 | # Tkinter uses SystemExit to exit | ||
| 152 | #print 'Exit' | ||
| 153 | self.exit = 1 | ||
| 154 | return | ||
| 155 | except KeyboardInterrupt: | ||
| 156 | if tkMessageBox.askquestion ('Interrupt', 'Really Quit?') == 'yes': | ||
| 157 | # self.tk.eval('exit') | ||
| 158 | self.exit = 1 | ||
| 159 | return | ||
| 160 | continue | ||
| 161 | except: | ||
| 162 | # Otherwise it's some other error - be nice and say why | ||
| 163 | t, v, tb = sys.exc_info() | ||
| 164 | text = "" | ||
| 165 | for line in traceback.format_exception(t,v,tb): | ||
| 166 | text += line + '\n' | ||
| 167 | try: tkMessageBox.showerror ('Error', text) | ||
| 168 | except: pass | ||
| 169 | self.exit = 1 | ||
| 170 | raise SystemExit, 1 | ||
| 171 | |||
| 172 | def destroy (self): | ||
| 173 | self.root.destroy() | ||
| 174 | |||
| 175 | def RunMain(root): | ||
| 176 | global demo | ||
| 177 | |||
| 178 | demo = Demo(root) | ||
| 179 | |||
| 180 | demo.build() | ||
| 181 | demo.loop() | ||
| 182 | demo.destroy() | ||
| 183 | |||
| 184 | # Tabs | ||
| 185 | def MkWelcome(nb, name): | ||
| 186 | w = nb.page(name) | ||
| 187 | bar = MkWelcomeBar(w) | ||
| 188 | text = MkWelcomeText(w) | ||
| 189 | bar.pack(side=TOP, fill=X, padx=2, pady=2) | ||
| 190 | text.pack(side=TOP, fill=BOTH, expand=1) | ||
| 191 | |||
| 192 | def MkWelcomeBar(top): | ||
| 193 | global demo | ||
| 194 | |||
| 195 | w = Tix.Frame(top, bd=2, relief=Tix.GROOVE) | ||
| 196 | b1 = Tix.ComboBox(w, command=lambda w=top: MainTextFont(w)) | ||
| 197 | b2 = Tix.ComboBox(w, command=lambda w=top: MainTextFont(w)) | ||
| 198 | b1.entry['width'] = 15 | ||
| 199 | b1.slistbox.listbox['height'] = 3 | ||
| 200 | b2.entry['width'] = 4 | ||
| 201 | b2.slistbox.listbox['height'] = 3 | ||
| 202 | |||
| 203 | demo.welfont = b1 | ||
| 204 | demo.welsize = b2 | ||
| 205 | |||
| 206 | b1.insert(Tix.END, 'Courier') | ||
| 207 | b1.insert(Tix.END, 'Helvetica') | ||
| 208 | b1.insert(Tix.END, 'Lucida') | ||
| 209 | b1.insert(Tix.END, 'Times Roman') | ||
| 210 | |||
| 211 | b2.insert(Tix.END, '8') | ||
| 212 | b2.insert(Tix.END, '10') | ||
| 213 | b2.insert(Tix.END, '12') | ||
| 214 | b2.insert(Tix.END, '14') | ||
| 215 | b2.insert(Tix.END, '18') | ||
| 216 | |||
| 217 | b1.pick(1) | ||
| 218 | b2.pick(3) | ||
| 219 | |||
| 220 | b1.pack(side=Tix.LEFT, padx=4, pady=4) | ||
| 221 | b2.pack(side=Tix.LEFT, padx=4, pady=4) | ||
| 222 | |||
| 223 | demo.balloon.bind_widget(b1, msg='Choose\na font', | ||
| 224 | statusmsg='Choose a font for this page') | ||
| 225 | demo.balloon.bind_widget(b2, msg='Point size', | ||
| 226 | statusmsg='Choose the font size for this page') | ||
| 227 | return w | ||
| 228 | |||
| 229 | def MkWelcomeText(top): | ||
| 230 | global demo | ||
| 231 | |||
| 232 | w = Tix.ScrolledWindow(top, scrollbar='auto') | ||
| 233 | win = w.window | ||
| 234 | text = 'Welcome to TIX in Python' | ||
| 235 | title = Tix.Label(win, | ||
| 236 | bd=0, width=30, anchor=Tix.N, text=text) | ||
| 237 | msg = Tix.Message(win, | ||
| 238 | bd=0, width=400, anchor=Tix.N, | ||
| 239 | text='Tix is a set of mega-widgets based on TK. This program \ | ||
| 240 | demonstrates the widgets in the Tix widget set. You can choose the pages \ | ||
| 241 | in this window to look at the corresponding widgets. \n\n\ | ||
| 242 | To quit this program, choose the "File | Exit" command.\n\n\ | ||
| 243 | For more information, see http://tix.sourceforge.net.') | ||
| 244 | title.pack(expand=1, fill=Tix.BOTH, padx=10, pady=10) | ||
| 245 | msg.pack(expand=1, fill=Tix.BOTH, padx=10, pady=10) | ||
| 246 | demo.welmsg = msg | ||
| 247 | return w | ||
| 248 | |||
| 249 | def MainTextFont(w): | ||
| 250 | global demo | ||
| 251 | |||
| 252 | if not demo.welmsg: | ||
| 253 | return | ||
| 254 | font = demo.welfont['value'] | ||
| 255 | point = demo.welsize['value'] | ||
| 256 | if font == 'Times Roman': | ||
| 257 | font = 'times' | ||
| 258 | fontstr = '%s %s' % (font, point) | ||
| 259 | demo.welmsg['font'] = fontstr | ||
| 260 | |||
| 261 | def ToggleHelp(): | ||
| 262 | if demo.useBalloons.get() == '1': | ||
| 263 | demo.balloon['state'] = 'both' | ||
| 264 | else: | ||
| 265 | demo.balloon['state'] = 'none' | ||
| 266 | |||
| 267 | def MkChoosers(nb, name): | ||
| 268 | w = nb.page(name) | ||
| 269 | options = "label.padX 4" | ||
| 270 | |||
| 271 | til = Tix.LabelFrame(w, label='Chooser Widgets', options=options) | ||
| 272 | cbx = Tix.LabelFrame(w, label='tixComboBox', options=options) | ||
| 273 | ctl = Tix.LabelFrame(w, label='tixControl', options=options) | ||
| 274 | sel = Tix.LabelFrame(w, label='tixSelect', options=options) | ||
| 275 | opt = Tix.LabelFrame(w, label='tixOptionMenu', options=options) | ||
| 276 | fil = Tix.LabelFrame(w, label='tixFileEntry', options=options) | ||
| 277 | fbx = Tix.LabelFrame(w, label='tixFileSelectBox', options=options) | ||
| 278 | tbr = Tix.LabelFrame(w, label='Tool Bar', options=options) | ||
| 279 | |||
| 280 | MkTitle(til.frame) | ||
| 281 | MkCombo(cbx.frame) | ||
| 282 | MkControl(ctl.frame) | ||
| 283 | MkSelect(sel.frame) | ||
| 284 | MkOptMenu(opt.frame) | ||
| 285 | MkFileEnt(fil.frame) | ||
| 286 | MkFileBox(fbx.frame) | ||
| 287 | MkToolBar(tbr.frame) | ||
| 288 | |||
| 289 | # First column: comBox and selector | ||
| 290 | cbx.form(top=0, left=0, right='%33') | ||
| 291 | sel.form(left=0, right='&'+str(cbx), top=cbx) | ||
| 292 | opt.form(left=0, right='&'+str(cbx), top=sel, bottom=-1) | ||
| 293 | |||
| 294 | # Second column: title .. etc | ||
| 295 | til.form(left=cbx, top=0,right='%66') | ||
| 296 | ctl.form(left=cbx, right='&'+str(til), top=til) | ||
| 297 | fil.form(left=cbx, right='&'+str(til), top=ctl) | ||
| 298 | tbr.form(left=cbx, right='&'+str(til), top=fil, bottom=-1) | ||
| 299 | |||
| 300 | # | ||
| 301 | # Third column: file selection | ||
| 302 | fbx.form(right=-1, top=0, left='%66') | ||
| 303 | |||
| 304 | def MkCombo(w): | ||
| 305 | options="label.width %d label.anchor %s entry.width %d" % (10, Tix.E, 14) | ||
| 306 | |||
| 307 | static = Tix.ComboBox(w, label='Static', editable=0, options=options) | ||
| 308 | editable = Tix.ComboBox(w, label='Editable', editable=1, options=options) | ||
| 309 | history = Tix.ComboBox(w, label='History', editable=1, history=1, | ||
| 310 | anchor=Tix.E, options=options) | ||
| 311 | static.insert(Tix.END, 'January') | ||
| 312 | static.insert(Tix.END, 'February') | ||
| 313 | static.insert(Tix.END, 'March') | ||
| 314 | static.insert(Tix.END, 'April') | ||
| 315 | static.insert(Tix.END, 'May') | ||
| 316 | static.insert(Tix.END, 'June') | ||
| 317 | static.insert(Tix.END, 'July') | ||
| 318 | static.insert(Tix.END, 'August') | ||
| 319 | static.insert(Tix.END, 'September') | ||
| 320 | static.insert(Tix.END, 'October') | ||
| 321 | static.insert(Tix.END, 'November') | ||
| 322 | static.insert(Tix.END, 'December') | ||
| 323 | |||
| 324 | editable.insert(Tix.END, 'Angola') | ||
| 325 | editable.insert(Tix.END, 'Bangladesh') | ||
| 326 | editable.insert(Tix.END, 'China') | ||
| 327 | editable.insert(Tix.END, 'Denmark') | ||
| 328 | editable.insert(Tix.END, 'Ecuador') | ||
| 329 | |||
| 330 | history.insert(Tix.END, '/usr/bin/ksh') | ||
| 331 | history.insert(Tix.END, '/usr/local/lib/python') | ||
| 332 | history.insert(Tix.END, '/var/adm') | ||
| 333 | |||
| 334 | static.pack(side=Tix.TOP, padx=5, pady=3) | ||
| 335 | editable.pack(side=Tix.TOP, padx=5, pady=3) | ||
| 336 | history.pack(side=Tix.TOP, padx=5, pady=3) | ||
| 337 | |||
| 338 | states = ['Bengal', 'Delhi', 'Karnataka', 'Tamil Nadu'] | ||
| 339 | |||
| 340 | def spin_cmd(w, inc): | ||
| 341 | idx = states.index(demo_spintxt.get()) + inc | ||
| 342 | if idx < 0: | ||
| 343 | idx = len(states) - 1 | ||
| 344 | elif idx >= len(states): | ||
| 345 | idx = 0 | ||
| 346 | # following doesn't work. | ||
| 347 | # return states[idx] | ||
| 348 | demo_spintxt.set(states[idx]) # this works | ||
| 349 | |||
| 350 | def spin_validate(w): | ||
| 351 | global states, demo_spintxt | ||
| 352 | |||
| 353 | try: | ||
| 354 | i = states.index(demo_spintxt.get()) | ||
| 355 | except ValueError: | ||
| 356 | return states[0] | ||
| 357 | return states[i] | ||
| 358 | # why this procedure works as opposed to the previous one beats me. | ||
| 359 | |||
| 360 | def MkControl(w): | ||
| 361 | global demo_spintxt | ||
| 362 | |||
| 363 | options="label.width %d label.anchor %s entry.width %d" % (10, Tix.E, 13) | ||
| 364 | |||
| 365 | demo_spintxt = Tix.StringVar() | ||
| 366 | demo_spintxt.set(states[0]) | ||
| 367 | simple = Tix.Control(w, label='Numbers', options=options) | ||
| 368 | spintxt = Tix.Control(w, label='States', variable=demo_spintxt, | ||
| 369 | options=options) | ||
| 370 | spintxt['incrcmd'] = lambda w=spintxt: spin_cmd(w, 1) | ||
| 371 | spintxt['decrcmd'] = lambda w=spintxt: spin_cmd(w, -1) | ||
| 372 | spintxt['validatecmd'] = lambda w=spintxt: spin_validate(w) | ||
| 373 | |||
| 374 | simple.pack(side=Tix.TOP, padx=5, pady=3) | ||
| 375 | spintxt.pack(side=Tix.TOP, padx=5, pady=3) | ||
| 376 | |||
| 377 | def MkSelect(w): | ||
| 378 | options = "label.anchor %s" % Tix.CENTER | ||
| 379 | |||
| 380 | sel1 = Tix.Select(w, label='Mere Mortals', allowzero=1, radio=1, | ||
| 381 | orientation=Tix.VERTICAL, | ||
| 382 | labelside=Tix.TOP, | ||
| 383 | options=options) | ||
| 384 | sel2 = Tix.Select(w, label='Geeks', allowzero=1, radio=0, | ||
| 385 | orientation=Tix.VERTICAL, | ||
| 386 | labelside= Tix.TOP, | ||
| 387 | options=options) | ||
| 388 | |||
| 389 | sel1.add('eat', text='Eat') | ||
| 390 | sel1.add('work', text='Work') | ||
| 391 | sel1.add('play', text='Play') | ||
| 392 | sel1.add('party', text='Party') | ||
| 393 | sel1.add('sleep', text='Sleep') | ||
| 394 | |||
| 395 | sel2.add('eat', text='Eat') | ||
| 396 | sel2.add('prog1', text='Program') | ||
| 397 | sel2.add('prog2', text='Program') | ||
| 398 | sel2.add('prog3', text='Program') | ||
| 399 | sel2.add('sleep', text='Sleep') | ||
| 400 | |||
| 401 | sel1.pack(side=Tix.LEFT, padx=5, pady=3, fill=Tix.X) | ||
| 402 | sel2.pack(side=Tix.LEFT, padx=5, pady=3, fill=Tix.X) | ||
| 403 | |||
| 404 | def MkOptMenu(w): | ||
| 405 | options='menubutton.width 15 label.anchor %s' % Tix.E | ||
| 406 | |||
| 407 | m = Tix.OptionMenu(w, label='File Format : ', options=options) | ||
| 408 | m.add_command('text', label='Plain Text') | ||
| 409 | m.add_command('post', label='PostScript') | ||
| 410 | m.add_command('format', label='Formatted Text') | ||
| 411 | m.add_command('html', label='HTML') | ||
| 412 | m.add_command('sep') | ||
| 413 | m.add_command('tex', label='LaTeX') | ||
| 414 | m.add_command('rtf', label='Rich Text Format') | ||
| 415 | |||
| 416 | m.pack(fill=Tix.X, padx=5, pady=3) | ||
| 417 | |||
| 418 | def MkFileEnt(w): | ||
| 419 | msg = Tix.Message(w, | ||
| 420 | relief=Tix.FLAT, width=240, anchor=Tix.N, | ||
| 421 | text='Press the "open file" icon button and a TixFileSelectDialog will popup.') | ||
| 422 | ent = Tix.FileEntry(w, label='Select a file : ') | ||
| 423 | msg.pack(side=Tix.TOP, expand=1, fill=Tix.BOTH, padx=3, pady=3) | ||
| 424 | ent.pack(side=Tix.TOP, fill=Tix.X, padx=3, pady=3) | ||
| 425 | |||
| 426 | def MkFileBox(w): | ||
| 427 | """The FileSelectBox is a Motif-style box with various enhancements. | ||
| 428 | For example, you can adjust the size of the two listboxes | ||
| 429 | and your past selections are recorded. | ||
| 430 | """ | ||
| 431 | msg = Tix.Message(w, | ||
| 432 | relief=Tix.FLAT, width=240, anchor=Tix.N, | ||
| 433 | text='The Tix FileSelectBox is a Motif-style box with various enhancements. For example, you can adjust the size of the two listboxes and your past selections are recorded.') | ||
| 434 | box = Tix.FileSelectBox(w) | ||
| 435 | msg.pack(side=Tix.TOP, expand=1, fill=Tix.BOTH, padx=3, pady=3) | ||
| 436 | box.pack(side=Tix.TOP, fill=Tix.X, padx=3, pady=3) | ||
| 437 | |||
| 438 | def MkToolBar(w): | ||
| 439 | """The Select widget is also good for arranging buttons in a tool bar. | ||
| 440 | """ | ||
| 441 | global demo | ||
| 442 | |||
| 443 | options='frame.borderWidth 1' | ||
560 more lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 453 lines aren’t shown here.
This diff is large: its 221 lines aren’t shown here.
This diff is large: its 144 lines aren’t shown here.
This diff is large: its 152 lines aren’t shown here.
This diff is large: its 51 lines aren’t shown here.
This diff is large: its 245 lines aren’t shown here.
This diff is large: its 110 lines aren’t shown here.
This diff is large: its 92 lines aren’t shown here.
This diff is large: its 155 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 99 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 286 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 160 lines aren’t shown here.
This diff is large: its 638 lines aren’t shown here.
This diff is large: its 635 lines aren’t shown here.
This diff is large: its 846 lines aren’t shown here.
This diff is large: its 129 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 268 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 79 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 65 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 47 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 245 lines aren’t shown here.
This diff is large: its 113 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 33 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 52 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 111 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 92 lines aren’t shown here.
This diff is large: its 134 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
Binary file not shown.
This diff is large: its 203 lines aren’t shown here.
This diff is large: its 737 lines aren’t shown here.
This diff is large: its 387 lines aren’t shown here.
This diff is large: its 247 lines aren’t shown here.
This diff is large: its 75 lines aren’t shown here.
This diff is large: its 1058 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 3204 lines aren’t shown here.
This diff is large: its 443 lines aren’t shown here.
This diff is large: its 885 lines aren’t shown here.
This diff is large: its 628 lines aren’t shown here.
This diff is large: its 205 lines aren’t shown here.
This diff is large: its 1781 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 1757 lines aren’t shown here.
This diff is large: its 1024 lines aren’t shown here.
This diff is large: its 288 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 15 lines aren’t shown here.
This diff is large: its 674 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 3796 lines aren’t shown here.
This diff is large: its 114 lines aren’t shown here.
This diff is large: its 2130 lines aren’t shown here.
This diff is large: its 144 lines aren’t shown here.
This diff is large: its 317 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 1391 lines aren’t shown here.
This diff is large: its 1766 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 191 lines aren’t shown here.
This diff is large: its 244 lines aren’t shown here.
This diff is large: its 225 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 92 lines aren’t shown here.
This diff is large: its 214 lines aren’t shown here.
This diff is large: its 321 lines aren’t shown here.
This diff is large: its 85 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 412 lines aren’t shown here.
This diff is large: its 486 lines aren’t shown here.
This diff is large: its 345 lines aren’t shown here.
This diff is large: its 1464 lines aren’t shown here.
This diff is large: its 466 lines aren’t shown here.
This diff is large: its 767 lines aren’t shown here.
This diff is large: its 599 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 85 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 141 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 244 lines aren’t shown here.
This diff is large: its 83 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 1113 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 284 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 355 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 116 lines aren’t shown here.
This diff is large: its 33 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 403 lines aren’t shown here.
This diff is large: its 245 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 88 lines aren’t shown here.
This diff is large: its 134 lines aren’t shown here.
This diff is large: its 179 lines aren’t shown here.
This diff is large: its 66 lines aren’t shown here.
This diff is large: its 562 lines aren’t shown here.
This diff is large: its 187 lines aren’t shown here.
This diff is large: its 209 lines aren’t shown here.
This diff is large: its 158 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 15 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 488 lines aren’t shown here.
This diff is large: its 203 lines aren’t shown here.
This diff is large: its 182 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 133 lines aren’t shown here.
This diff is large: its 86 lines aren’t shown here.
This diff is large: its 242 lines aren’t shown here.
This diff is large: its 176 lines aren’t shown here.
This diff is large: its 58 lines aren’t shown here.
This diff is large: its 255 lines aren’t shown here.
This diff is large: its 261 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 260 lines aren’t shown here.
This diff is large: its 170 lines aren’t shown here.
This diff is large: its 246 lines aren’t shown here.
This diff is large: its 58 lines aren’t shown here.
This diff is large: its 148 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 85 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 209 lines aren’t shown here.
This diff is large: its 175 lines aren’t shown here.
This diff is large: its 305 lines aren’t shown here.
This diff is large: its 305 lines aren’t shown here.
This diff is large: its 314 lines aren’t shown here.
This diff is large: its 603 lines aren’t shown here.
This diff is large: its 71 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 112 lines aren’t shown here.
This diff is large: its 143 lines aren’t shown here.
This diff is large: its 199 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 174 lines aren’t shown here.
This diff is large: its 1349 lines aren’t shown here.
This diff is large: its 104 lines aren’t shown here.
This diff is large: its 342 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 131 lines aren’t shown here.
This diff is large: its 261 lines aren’t shown here.
This diff is large: its 721 lines aren’t shown here.
This diff is large: its 98 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 539 lines aren’t shown here.
This diff is large: its 2444 lines aren’t shown here.
This diff is large: its 1396 lines aren’t shown here.
This diff is large: its 97 lines aren’t shown here.
This diff is large: its 1442 lines aren’t shown here.
This diff is large: its 89 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.
This diff is large: its 1314 lines aren’t shown here.
This diff is large: its 705 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 707 lines aren’t shown here.
This diff is large: its 102 lines aren’t shown here.
This diff is large: its 1950 lines aren’t shown here.
This diff is large: its 106 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 150 lines aren’t shown here.
This diff is large: its 357 lines aren’t shown here.
This diff is large: its 467 lines aren’t shown here.
This diff is large: its 175 lines aren’t shown here.
This diff is large: its 143 lines aren’t shown here.
This diff is large: its 193 lines aren’t shown here.
This diff is large: its 508 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 87 lines aren’t shown here.
This diff is large: its 330 lines aren’t shown here.
This diff is large: its 128 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 289 lines aren’t shown here.
This diff is large: its 1322 lines aren’t shown here.
This diff is large: its 133 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 197 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 155 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 774 lines aren’t shown here.
This diff is large: its 225 lines aren’t shown here.
This diff is large: its 52 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 71 lines aren’t shown here.
This diff is large: its 115 lines aren’t shown here.
This diff is large: its 215 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 138 lines aren’t shown here.
This diff is large: its 182 lines aren’t shown here.
This diff is large: its 170 lines aren’t shown here.
This diff is large: its 435 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 508 lines aren’t shown here.
This diff is large: its 67 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 292 lines aren’t shown here.
This diff is large: its 394 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 547 lines aren’t shown here.
This diff is large: its 81 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 51 lines aren’t shown here.
This diff is large: its 528 lines aren’t shown here.
This diff is large: its 1730 lines aren’t shown here.
This diff is large: its 1445 lines aren’t shown here.
This diff is large: its 83 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 118 lines aren’t shown here.
This diff is large: its 210 lines aren’t shown here.
This diff is large: its 93 lines aren’t shown here.
This diff is large: its 169 lines aren’t shown here.
This diff is large: its 121 lines aren’t shown here.
This diff is large: its 227 lines aren’t shown here.
This diff is large: its 81 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 172 lines aren’t shown here.
This diff is large: its 52 lines aren’t shown here.
This diff is large: its 486 lines aren’t shown here.
This diff is large: its 110 lines aren’t shown here.
This diff is large: its 176 lines aren’t shown here.
This diff is large: its 58 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 356 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 531 lines aren’t shown here.
This diff is large: its 1889 lines aren’t shown here.
This diff is large: its 2007 lines aren’t shown here.
This diff is large: its 402 lines aren’t shown here.
This diff is large: its 75 lines aren’t shown here.
This diff is large: its 712 lines aren’t shown here.
This diff is large: its 457 lines aren’t shown here.
This diff is large: its 889 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 85 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 222 lines aren’t shown here.
This diff is large: its 190 lines aren’t shown here.
This diff is large: its 182 lines aren’t shown here.
This diff is large: its 98 lines aren’t shown here.
This diff is large: its 176 lines aren’t shown here.
This diff is large: its 282 lines aren’t shown here.
This diff is large: its 211 lines aren’t shown here.
This diff is large: its 723 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 58 lines aren’t shown here.
This diff is large: its 103 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 778 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 146 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.
This diff is large: its 310 lines aren’t shown here.
This diff is large: its 955 lines aren’t shown here.
This diff is large: its 197 lines aren’t shown here.
This diff is large: its 134 lines aren’t shown here.
This diff is large: its 216 lines aren’t shown here.
This diff is large: its 67 lines aren’t shown here.
This diff is large: its 276 lines aren’t shown here.
This diff is large: its 337 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 66 lines aren’t shown here.
This diff is large: its 67 lines aren’t shown here.
This diff is large: its 75 lines aren’t shown here.
This diff is large: its 98 lines aren’t shown here.
This diff is large: its 133 lines aren’t shown here.
This diff is large: its 265 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 272 lines aren’t shown here.
This diff is large: its 84 lines aren’t shown here.
This diff is large: its 175 lines aren’t shown here.
This diff is large: its 275 lines aren’t shown here.
This diff is large: its 152 lines aren’t shown here.
This diff is large: its 175 lines aren’t shown here.
This diff is large: its 87 lines aren’t shown here.
This diff is large: its 229 lines aren’t shown here.
This diff is large: its 89 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 296 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 911 lines aren’t shown here.
This diff is large: its 294 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
This diff is large: its 643 lines aren’t shown here.
This diff is large: its 158 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 2096 lines aren’t shown here.
This diff is large: its 833 lines aren’t shown here.
This diff is large: its 452 lines aren’t shown here.
This diff is large: its 122 lines aren’t shown here.
This diff is large: its 137 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 257 lines aren’t shown here.
This diff is large: its 403 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 219 lines aren’t shown here.
This diff is large: its 147 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 618 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 498 lines aren’t shown here.
This diff is large: its 216 lines aren’t shown here.
This diff is large: its 195 lines aren’t shown here.
This diff is large: its 107 lines aren’t shown here.
This diff is large: its 279 lines aren’t shown here.
This diff is large: its 182 lines aren’t shown here.
This diff is large: its 174 lines aren’t shown here.
This diff is large: its 717 lines aren’t shown here.
This diff is large: its 462 lines aren’t shown here.
This diff is large: its 225 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 120 lines aren’t shown here.
This diff is large: its 126 lines aren’t shown here.
This diff is large: its 158 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 269 lines aren’t shown here.
This diff is large: its 216 lines aren’t shown here.
This diff is large: its 114 lines aren’t shown here.
This diff is large: its 161 lines aren’t shown here.
This diff is large: its 975 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 498 lines aren’t shown here.
This diff is large: its 873 lines aren’t shown here.
This diff is large: its 254 lines aren’t shown here.
This diff is large: its 72 lines aren’t shown here.
This diff is large: its 182 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 234 lines aren’t shown here.
This diff is large: its 254 lines aren’t shown here.
This diff is large: its 170 lines aren’t shown here.
This diff is large: its 337 lines aren’t shown here.
This diff is large: its 174 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 415 lines aren’t shown here.
This diff is large: its 143 lines aren’t shown here.
This diff is large: its 783 lines aren’t shown here.
This diff is large: its 252 lines aren’t shown here.
This diff is large: its 288 lines aren’t shown here.
This diff is large: its 384 lines aren’t shown here.
This diff is large: its 310 lines aren’t shown here.
This diff is large: its 134 lines aren’t shown here.
This diff is large: its 198 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 66 lines aren’t shown here.
This diff is large: its 65 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 15 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 4 lines aren’t shown here.
This diff is large: its 4 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 1874 lines aren’t shown here.
This diff is large: its 140 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 929 lines aren’t shown here.
This diff is large: its 286 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 144 lines aren’t shown here.
This diff is large: its 382 lines aren’t shown here.
This diff is large: its 352 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 83 lines aren’t shown here.
This diff is large: its 84 lines aren’t shown here.
This diff is large: its 136 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 315 lines aren’t shown here.
This diff is large: its 65 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 242 lines aren’t shown here.
This diff is large: its 124 lines aren’t shown here.
This diff is large: its 91 lines aren’t shown here.
This diff is large: its 106 lines aren’t shown here.
This diff is large: its 267 lines aren’t shown here.
This diff is large: its 66 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 90 lines aren’t shown here.
This diff is large: its 102 lines aren’t shown here.
This diff is large: its 174 lines aren’t shown here.
This diff is large: its 98 lines aren’t shown here.
This diff is large: its 219 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 96 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 802 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 2173 lines aren’t shown here.
This diff is large: its 184 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 137 lines aren’t shown here.
This diff is large: its 732 lines aren’t shown here.
This diff is large: its 2226 lines aren’t shown here.
This diff is large: its 220 lines aren’t shown here.
This diff is large: its 1326 lines aren’t shown here.
This diff is large: its 929 lines aren’t shown here.
This diff is large: its 545 lines aren’t shown here.
This diff is large: its 113 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 113 lines aren’t shown here.
This diff is large: its 90 lines aren’t shown here.
This diff is large: its 171 lines aren’t shown here.
This diff is large: its 150 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 330 lines aren’t shown here.
This diff is large: its 434 lines aren’t shown here.
This diff is large: its 110 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 156 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 1328 lines aren’t shown here.
This diff is large: its 233 lines aren’t shown here.
This diff is large: its 72 lines aren’t shown here.
This diff is large: its 389 lines aren’t shown here.
This diff is large: its 113 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 158 lines aren’t shown here.
This diff is large: its 79 lines aren’t shown here.
This diff is large: its 137 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 129 lines aren’t shown here.
This diff is large: its 3 lines aren’t shown here.
This diff is large: its 4 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 98 lines aren’t shown here.
This diff is large: its 72 lines aren’t shown here.
This diff is large: its 1751 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 184 lines aren’t shown here.
This diff is large: its 127 lines aren’t shown here.
This diff is large: its 130 lines aren’t shown here.
This diff is large: its 67 lines aren’t shown here.
This diff is large: its 660 lines aren’t shown here.
This diff is large: its 66 lines aren’t shown here.
This diff is large: its 159 lines aren’t shown here.
This diff is large: its 86 lines aren’t shown here.
This diff is large: its 165 lines aren’t shown here.
This diff is large: its 72 lines aren’t shown here.
This diff is large: its 520 lines aren’t shown here.
This diff is large: its 139 lines aren’t shown here.
This diff is large: its 971 lines aren’t shown here.
This diff is large: its 99 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 915 lines aren’t shown here.
This diff is large: its 1074 lines aren’t shown here.
This diff is large: its 265 lines aren’t shown here.
This diff is large: its 313 lines aren’t shown here.
This diff is large: its 566 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
This diff is large: its 203 lines aren’t shown here.
This diff is large: its 161 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 3 lines aren’t shown here.
This diff is large: its 353 lines aren’t shown here.
This diff is large: its 5908 lines aren’t shown here.
This diff is large: its 4 lines aren’t shown here.
This diff is large: its 1338 lines aren’t shown here.
This diff is large: its 869 lines aren’t shown here.
This diff is large: its 1467 lines aren’t shown here.
This diff is large: its 2381 lines aren’t shown here.
This diff is large: its 1758 lines aren’t shown here.
This diff is large: its 2531 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 156 lines aren’t shown here.
This diff is large: its 402 lines aren’t shown here.
This diff is large: its 157 lines aren’t shown here.
This diff is large: its 445 lines aren’t shown here.
This diff is large: its 535 lines aren’t shown here.
This diff is large: its 436 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 149 lines aren’t shown here.
This diff is large: its 502 lines aren’t shown here.
This diff is large: its 174 lines aren’t shown here.
This diff is large: its 1277 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 33 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 71 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 158 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 168 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 246 lines aren’t shown here.
This diff is large: its 92 lines aren’t shown here.
This diff is large: its 142 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 85 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 65 lines aren’t shown here.
This diff is large: its 65 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 115 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 92 lines aren’t shown here.
This diff is large: its 134 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 869 lines aren’t shown here.
This diff is large: its 337 lines aren’t shown here.
This diff is large: its 153 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 51 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 177 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 335 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 177 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 190 lines aren’t shown here.
This diff is large: its 106 lines aren’t shown here.
This diff is large: its 772 lines aren’t shown here.
This diff is large: its 196 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 172 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 44 lines aren’t shown here.
This diff is large: its 184 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 104 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 84 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 58 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 1266 lines aren’t shown here.
This diff is large: its 76 lines aren’t shown here.
This diff is large: its 271 lines aren’t shown here.
This diff is large: its 699 lines aren’t shown here.
This diff is large: its 156 lines aren’t shown here.
This diff is large: its 44 lines aren’t shown here.
This diff is large: its 575 lines aren’t shown here.
This diff is large: its 2783 lines aren’t shown here.
This diff is large: its 74 lines aren’t shown here.
This diff is large: its 98 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 98 lines aren’t shown here.
This diff is large: its 115 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 396 lines aren’t shown here.
This diff is large: its 262 lines aren’t shown here.
This diff is large: its 148 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 227 lines aren’t shown here.
This diff is large: its 96 lines aren’t shown here.
This diff is large: its 228 lines aren’t shown here.
This diff is large: its 2280 lines aren’t shown here.
This diff is large: its 142 lines aren’t shown here.
This diff is large: its 88 lines aren’t shown here.
This diff is large: its 141 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 86 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 3769 lines aren’t shown here.
This diff is large: its 398 lines aren’t shown here.
This diff is large: its 177 lines aren’t shown here.
This diff is large: its 350 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 489 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 488 lines aren’t shown here.
This diff is large: its 2 lines aren’t shown here.
This diff is large: its 236 lines aren’t shown here.
This diff is large: its 6048 lines aren’t shown here.
This diff is large: its 935 lines aren’t shown here.
This diff is large: its 1605 lines aren’t shown here.
This diff is larger than a page shows: the files after these aren’t listed. Clone the repository to see all of it.