diff options
-rw-r--r-- | Lib/calendar.py | 8 | ||||
-rw-r--r-- | Lib/imghdr.py | 26 | ||||
-rwxr-xr-x | Lib/irix5/torgb.py | 91 | ||||
-rw-r--r-- | Lib/pipes.py | 302 | ||||
-rwxr-xr-x | Lib/plat-irix5/torgb.py | 91 | ||||
-rw-r--r-- | Lib/string.py | 12 | ||||
-rw-r--r-- | Lib/stringold.py | 12 | ||||
-rw-r--r-- | Lib/toaiff.py | 101 | ||||
-rw-r--r-- | Lib/tzparse.py | 76 | ||||
-rw-r--r-- | Lib/whrandom.py | 105 |
10 files changed, 778 insertions, 46 deletions
diff --git a/Lib/calendar.py b/Lib/calendar.py index 4e02e0e..62fb27f 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -11,7 +11,7 @@ # - Monday is the first day of the week (numbered 0) # These are really parameters of the 'time' module: -epoch = 1970 # Time began on January 1 of this year (00:00:00 UCT) +epoch = 1970 # Time began on January 1 of this year (00:00:00 UTC) day_0 = 3 # The epoch begins on a Thursday (Monday = 0) # Return 1 for leap years, 0 for non-leap years @@ -58,7 +58,7 @@ def leapdays(y1, y2): return (y2+3)/4 - (y1+3)/4 # Inverse of gmtime(): -# Turn UCT calendar time (less yday, wday) into seconds since epoch +# Turn UTC calendar time (less yday, wday) into seconds since epoch def mktime(year, month, day, hours, mins, secs): days = day - 1 for m in range(January, month): days = days + mdays[m] @@ -96,8 +96,8 @@ def asctime(year, month, day, hours, mins, secs, yday, wday): return s + ' ' + `year` # Localization: Minutes West from Greenwich -# timezone = -2*60 # Middle-European time with DST on -timezone = 5*60 # EST (sigh -- THINK time() doesn't return UCT) +timezone = -2*60 # Middle-European time with DST on +# timezone = 5*60 # EST (sigh -- THINK time() doesn't return UTC) # Local time ignores DST issues for now -- adjust 'timezone' to fake it def localtime(secs): diff --git a/Lib/imghdr.py b/Lib/imghdr.py index 981340f..063bfe8 100644 --- a/Lib/imghdr.py +++ b/Lib/imghdr.py @@ -35,13 +35,29 @@ def test_gif(h, f): tests.append(test_gif) -def test_pnm(h, f): - # PBM, PGM, PPM (portable {bit,gray,pix}map; together portable anymap) +def test_pbm(h, f): + # PBM (portable bitmap) if len(h) >= 3 and \ - h[0] == 'P' and h[1] in '123456' and h[2] in ' \t\n\r': - return 'pnm' + h[0] == 'P' and h[1] in '14' and h[2] in ' \t\n\r': + return 'pbm' -tests.append(test_pnm) +tests.append(test_pbm) + +def test_pgm(h, f): + # PGM (portable graymap) + if len(h) >= 3 and \ + h[0] == 'P' and h[1] in '25' and h[2] in ' \t\n\r': + return 'pgm' + +tests.append(test_pgm) + +def test_ppm(h, f): + # PPM (portable pixmap) + if len(h) >= 3 and \ + h[0] == 'P' and h[1] in '36' and h[2] in ' \t\n\r': + return 'ppm' + +tests.append(test_ppm) def test_tiff(h, f): # TIFF (can be in Motorola or Intel byte order) diff --git a/Lib/irix5/torgb.py b/Lib/irix5/torgb.py new file mode 100755 index 0000000..e46aca3 --- /dev/null +++ b/Lib/irix5/torgb.py @@ -0,0 +1,91 @@ +# Convert "arbitrary" image files to rgb files (SGI's image format). +# Input may be compressed. +# The uncompressed file type may be PBM, PGM, PPM, GIF, TIFF, or Sun raster. +# An exception is raised if the file is not of a recognized type. +# Returned filename is either the input filename or a temporary filename; +# in the latter case the caller must ensure that it is removed. +# Other temporary files used are removed by the function. + +import os +import tempfile +import pipes +import imghdr + +table = {} + +t = pipes.Template().init() +t.append('fromppm $IN $OUT', 'ff') +table['ppm'] = t + +t = pipes.Template().init() +t.append('pnmtoppm', '--') +t.append('fromppm $IN $OUT', 'ff') +table['pnm'] = t +table['pgm'] = t +table['pbm'] = t + +t = pipes.Template().init() +t.append('fromgif $IN $OUT', 'ff') +table['gif'] = t + +t = pipes.Template().init() +t.append('tifftopnm', '--') +t.append('(PATH=$PATH:/ufs/guido/bin/sgi; exec pnmtoppm)', '--') +t.append('fromppm $IN $OUT', 'ff') +table['tiff'] = t + +t = pipes.Template().init() +t.append('rasttopnm', '--') +t.append('pnmtoppm', '--') +t.append('fromppm $IN $OUT', 'ff') +table['rast'] = t + +uncompress = pipes.Template().init() +uncompress.append('uncompress', '--') + + +error = 'torgb.error' # Exception + +def torgb(filename): + temps = [] + ret = None + try: + ret = _torgb(filename, temps) + finally: + for temp in temps[:]: + if temp <> ret: + try: + os.unlink(temp) + except os.error: + pass + temps.remove(temp) + return ret + +def _torgb(filename, temps): + if filename[-2:] == '.Z': + fname = tempfile.mktemp() + temps.append(fname) + sts = uncompress.copy(filename, fname) + if sts: + raise error, filename + ': uncompress failed' + else: + fname = filename + try: + ftype = imghdr.what(fname) + except IOError, msg: + if type(msg) == type(()) and len(msg) == 2 and \ + type(msg[0]) == type(0) and type(msg[1]) == type(''): + msg = msg[1] + if type(msg) <> type(''): + msg = `msg` + raise error, filename + ': ' + msg + if ftype == 'rgb': + return fname + if ftype == None or not table.has_key(ftype): + raise error, \ + filename + ': unsupported image file type ' + `ftype` + temp = tempfile.mktemp() + sts = table[ftype].copy(fname, temp) + if sts: + raise error, filename + ': conversion to rgb failed' + return temp diff --git a/Lib/pipes.py b/Lib/pipes.py new file mode 100644 index 0000000..426c377 --- /dev/null +++ b/Lib/pipes.py @@ -0,0 +1,302 @@ +# Conversion pipeline templates +# ============================= + + +# The problem: +# ------------ +# +# Suppose you have some data that you want to convert to another format +# (e.g. from GIF image format to PPM image format). Maybe the +# conversion involves several steps (e.g. piping it through compress or +# uuencode). Some of the conversion steps may require that their input +# is a disk file, others may be able to read standard input; similar for +# their output. The input to the entire conversion may also be read +# from a disk file or from an open file, and similar for its output. +# +# The module lets you construct a pipeline template by sticking one or +# more conversion steps together. It will take care of creating and +# removing temporary files if they are necessary to hold intermediate +# data. You can then use the template to do conversions from many +# different sources to many different destinations. The temporary +# file names used are different each time the template is used. +# +# The templates are objects so you can create templates for many +# different conversion steps and store them in a dictionary, for +# instance. + + +# Directions: +# ----------- +# +# To create a template: +# t = Template().init() +# +# To add a conversion step to a template: +# t.append(command, kind) +# where kind is a string of two characters: the first is '-' if the +# command reads its standard input or 'f' if it requires a file; the +# second likewise for the output. The command must be valid /bin/sh +# syntax. If input or output files are required, they are passed as +# $IN and $OUT; otherwise, it must be possible to use the command in +# a pipeline. +# +# To add a conversion step at the beginning: +# t.prepend(command, kind) +# +# To convert a file to another file using a template: +# sts = t.copy(infile, outfile) +# If infile or outfile are the empty string, standard input is read or +# standard output is written, respectively. The return value is the +# exit status of the conversion pipeline. +# +# To open a file for reading or writing through a conversion pipeline: +# fp = t.open(file, mode) +# where mode is 'r' to read the file, or 'w' to write it -- just like +# for the built-in function open() or for os.popen(). +# +# To create a new template object initialized to a given one: +# t2 = t.clone() +# +# For an example, see the function test() at the end of the file. + + +import sys +import regex + +import os +import tempfile +import string + + +# Conversion step kinds + +FILEIN_FILEOUT = 'ff' # Must read & write real files +STDIN_FILEOUT = '-f' # Must write a real file +FILEIN_STDOUT = 'f-' # Must read a real file +STDIN_STDOUT = '--' # Normal pipeline element +SOURCE = '.-' # Must be first, writes stdout +SINK = '-.' # Must be last, reads stdin + +stepkinds = [FILEIN_FILEOUT, STDIN_FILEOUT, FILEIN_STDOUT, STDIN_STDOUT, \ + SOURCE, SINK] + + +# A pipeline template is a Template object: + +class Template: + + # Template().init() returns a fresh pipeline template + def init(self): + self.debugging = 0 + self.reset() + return self + + # t.__repr__() implements `t` + def __repr__(self): + return '<Template instance, steps=' + `self.steps` + '>' + + # t.reset() restores a pipeline template to its initial state + def reset(self): + self.steps = [] + + # t.clone() returns a new pipeline template with identical + # initial state as the current one + def clone(self): + t = Template().init() + t.steps = self.steps[:] + t.debugging = self.debugging + return t + + # t.debug(flag) turns debugging on or off + def debug(self, flag): + self.debugging = flag + + # t.append(cmd, kind) adds a new step at the end + def append(self, cmd, kind): + if type(cmd) <> type(''): + raise TypeError, \ + 'Template.append: cmd must be a string' + if kind not in stepkinds: + raise ValueError, \ + 'Template.append: bad kind ' + `kind` + if kind == SOURCE: + raise ValueError, \ + 'Template.append: SOURCE can only be prepended' + if self.steps <> [] and self.steps[-1][1] == SINK: + raise ValueError, \ + 'Template.append: already ends with SINK' + if kind[0] == 'f' and regex.search('\$IN', cmd) < 0: + raise ValueError, \ + 'Template.append: missing $IN in cmd' + if kind[1] == 'f' and regex.search('\$OUT', cmd) < 0: + raise ValueError, \ + 'Template.append: missing $OUT in cmd' + self.steps.append((cmd, kind)) + + # t.prepend(cmd, kind) adds a new step at the front + def prepend(self, cmd, kind): + if type(cmd) <> type(''): + raise TypeError, \ + 'Template.prepend: cmd must be a string' + if kind not in stepkinds: + raise ValueError, \ + 'Template.prepend: bad kind ' + `kind` + if kind == SINK: + raise ValueError, \ + 'Template.prepend: SINK can only be appended' + if self.steps <> [] and self.steps[0][1] == SOURCE: + raise ValueError, \ + 'Template.prepend: already begins with SOURCE' + if kind[0] == 'f' and regex.search('\$IN\>', cmd) < 0: + raise ValueError, \ + 'Template.prepend: missing $IN in cmd' + if kind[1] == 'f' and regex.search('\$OUT\>', cmd) < 0: + raise ValueError, \ + 'Template.prepend: missing $OUT in cmd' + self.steps.insert(0, (cmd, kind)) + + # t.open(file, rw) returns a pipe or file object open for + # reading or writing; the file is the other end of the pipeline + def open(self, file, rw): + if rw == 'r': + return self.open_r(file) + if rw == 'w': + return self.open_w(file) + raise ValueError, \ + 'Template.open: rw must be \'r\' or \'w\', not ' + `rw` + + # t.open_r(file) and t.open_w(file) implement + # t.open(file, 'r') and t.open(file, 'w') respectively + + def open_r(self, file): + if self.steps == []: + return open(file, 'r') + if self.steps[-1][1] == SINK: + raise ValueError, \ + 'Template.open_r: pipeline ends width SINK' + cmd = self.makepipeline(file, '') + return os.popen(cmd, 'r') + + def open_w(self, file): + if self.steps == []: + return open(file, 'w') + if self.steps[0][1] == SOURCE: + raise ValueError, \ + 'Template.open_w: pipeline begins with SOURCE' + cmd = self.makepipeline('', file) + return os.popen(cmd, 'w') + + def copy(self, infile, outfile): + return os.system(self.makepipeline(infile, outfile)) + + def makepipeline(self, infile, outfile): + cmd = makepipeline(infile, self.steps, outfile) + if self.debugging: + print cmd + cmd = 'set -x; ' + cmd + return cmd + + +def makepipeline(infile, steps, outfile): + # Build a list with for each command: + # [input filename or '', command string, kind, output filename or ''] + + list = [] + for cmd, kind in steps: + list.append(['', cmd, kind, '']) + # + # Make sure there is at least one step + # + if list == []: + list.append(['', 'cat', '--', '']) + # + # Take care of the input and output ends + # + [cmd, kind] = list[0][1:3] + if kind[0] == 'f' and not infile: + list.insert(0, ['', 'cat', '--', '']) + list[0][0] = infile + # + [cmd, kind] = list[-1][1:3] + if kind[1] == 'f' and not outfile: + list.append(['', 'cat', '--', '']) + list[-1][-1] = outfile + # + # Invent temporary files to connect stages that need files + # + garbage = [] + for i in range(1, len(list)): + lkind = list[i-1][2] + rkind = list[i][2] + if lkind[1] == 'f' or rkind[0] == 'f': + temp = tempfile.mktemp() + garbage.append(temp) + list[i-1][-1] = list[i][0] = temp + # + for item in list: + [inf, cmd, kind, outf] = item + if kind[1] == 'f': + cmd = 'OUT=' + quote(outf) + '; ' + cmd + if kind[0] == 'f': + cmd = 'IN=' + quote(inf) + '; ' + cmd + if kind[0] == '-' and inf: + cmd = cmd + ' <' + quote(inf) + if kind[1] == '-' and outf: + cmd = cmd + ' >' + quote(outf) + item[1] = cmd + # + cmdlist = list[0][1] + for item in list[1:]: + [cmd, kind] = item[1:3] + if item[0] == '': + if 'f' in kind: + cmd = '{ ' + cmd + '; }' + cmdlist = cmdlist + ' |\n' + cmd + else: + cmdlist = cmdlist + '\n' + cmd + # + if garbage: + rmcmd = 'rm -f' + for file in garbage: + rmcmd = rmcmd + ' ' + quote(file) + trapcmd = 'trap ' + quote(rmcmd + '; exit') + ' 1 2 3 13 14 15' + cmdlist = trapcmd + '\n' + cmdlist + '\n' + rmcmd + # + return cmdlist + + +# Reliably quote a string as a single argument for /bin/sh + +_safechars = string.letters + string.digits + '!@%_-+=:,./' # Safe unquoted +_funnychars = '"`$\\' # Unsafe inside "double quotes" + +def quote(file): + for c in file: + if c not in _safechars: + break + else: + return file + if '\'' not in file: + return '\'' + file + '\'' + res = '' + for c in file: + if c in _funnychars: + c = '\\' + c + res = res + c + return '"' + res + '"' + + +# Small test program and example + +def test(): + import os + print 'Testing...' + t = Template().init() + t.append('togif $IN $OUT', 'ff') + t.append('giftoppm', '--') + t.append('ppmtogif >$OUT', '-f') + t.append('fromgif $IN $OUT', 'ff') + t.debug(1) + FILE = '/usr/local/images/rgb/rogues/guido.rgb' + t.copy(FILE, '@temp') + print 'Done.' diff --git a/Lib/plat-irix5/torgb.py b/Lib/plat-irix5/torgb.py new file mode 100755 index 0000000..e46aca3 --- /dev/null +++ b/Lib/plat-irix5/torgb.py @@ -0,0 +1,91 @@ +# Convert "arbitrary" image files to rgb files (SGI's image format). +# Input may be compressed. +# The uncompressed file type may be PBM, PGM, PPM, GIF, TIFF, or Sun raster. +# An exception is raised if the file is not of a recognized type. +# Returned filename is either the input filename or a temporary filename; +# in the latter case the caller must ensure that it is removed. +# Other temporary files used are removed by the function. + +import os +import tempfile +import pipes +import imghdr + +table = {} + +t = pipes.Template().init() +t.append('fromppm $IN $OUT', 'ff') +table['ppm'] = t + +t = pipes.Template().init() +t.append('pnmtoppm', '--') +t.append('fromppm $IN $OUT', 'ff') +table['pnm'] = t +table['pgm'] = t +table['pbm'] = t + +t = pipes.Template().init() +t.append('fromgif $IN $OUT', 'ff') +table['gif'] = t + +t = pipes.Template().init() +t.append('tifftopnm', '--') +t.append('(PATH=$PATH:/ufs/guido/bin/sgi; exec pnmtoppm)', '--') +t.append('fromppm $IN $OUT', 'ff') +table['tiff'] = t + +t = pipes.Template().init() +t.append('rasttopnm', '--') +t.append('pnmtoppm', '--') +t.append('fromppm $IN $OUT', 'ff') +table['rast'] = t + +uncompress = pipes.Template().init() +uncompress.append('uncompress', '--') + + +error = 'torgb.error' # Exception + +def torgb(filename): + temps = [] + ret = None + try: + ret = _torgb(filename, temps) + finally: + for temp in temps[:]: + if temp <> ret: + try: + os.unlink(temp) + except os.error: + pass + temps.remove(temp) + return ret + +def _torgb(filename, temps): + if filename[-2:] == '.Z': + fname = tempfile.mktemp() + temps.append(fname) + sts = uncompress.copy(filename, fname) + if sts: + raise error, filename + ': uncompress failed' + else: + fname = filename + try: + ftype = imghdr.what(fname) + except IOError, msg: + if type(msg) == type(()) and len(msg) == 2 and \ + type(msg[0]) == type(0) and type(msg[1]) == type(''): + msg = msg[1] + if type(msg) <> type(''): + msg = `msg` + raise error, filename + ': ' + msg + if ftype == 'rgb': + return fname + if ftype == None or not table.has_key(ftype): + raise error, \ + filename + ': unsupported image file type ' + `ftype` + temp = tempfile.mktemp() + sts = table[ftype].copy(fname, temp) + if sts: + raise error, filename + ': conversion to rgb failed' + return temp diff --git a/Lib/string.py b/Lib/string.py index 6386a95..cc60678 100644 --- a/Lib/string.py +++ b/Lib/string.py @@ -163,3 +163,15 @@ def expandtabs(s, tabsize): res = res + line line = '' return res + line + + +# Try importing optional built-in module "strop" -- if it exists, +# it redefines some string operations that are 100-1000 times faster. +# The manipulation with index_error is needed for compatibility. + +try: + from strop import * + from strop import index + index_error = ValueError +except ImportError: + pass # Use the original, slow versions diff --git a/Lib/stringold.py b/Lib/stringold.py index 6386a95..cc60678 100644 --- a/Lib/stringold.py +++ b/Lib/stringold.py @@ -163,3 +163,15 @@ def expandtabs(s, tabsize): res = res + line line = '' return res + line + + +# Try importing optional built-in module "strop" -- if it exists, +# it redefines some string operations that are 100-1000 times faster. +# The manipulation with index_error is needed for compatibility. + +try: + from strop import * + from strop import index + index_error = ValueError +except ImportError: + pass # Use the original, slow versions diff --git a/Lib/toaiff.py b/Lib/toaiff.py new file mode 100644 index 0000000..adb2e61 --- /dev/null +++ b/Lib/toaiff.py @@ -0,0 +1,101 @@ +# Convert "arbitrary" sound files to AIFF files (Apple and SGI's audio format). +# Input may be compressed. +# Uncompressed file type may be AIFF, WAV, VOC, 8SVX, NeXT/Sun, and others. +# An exception is raised if the file is not of a recognized type. +# Returned filename is either the input filename or a temporary filename; +# in the latter case the caller must ensure that it is removed. +# Other temporary files used are removed by the function. + +import os +import tempfile +import pipes +import sndhdr + +table = {} + +t = pipes.Template().init() +t.append('sox -t au - -t aiff -r 8000 -', '--') +table['au'] = t + +# XXX The following is actually sub-optimal. +# XXX The HCOM sampling rate can be 22k, 22k/2, 22k/3 or 22k/4. +# XXX We must force the output sampling rate else the SGI won't play +# XXX files sampled at 5.5k or 7.333k; however this means that files +# XXX sampled at 11k are unnecessarily expanded. +# XXX Similar comments apply to some other file types. +t = pipes.Template().init() +t.append('sox -t hcom - -t aiff -r 22050 -', '--') +table['hcom'] = t + +t = pipes.Template().init() +t.append('sox -t voc - -t aiff -r 11025 -', '--') +table['voc'] = t + +t = pipes.Template().init() +t.append('sox -t wav - -t aiff -', '--') +table['wav'] = t + +t = pipes.Template().init() +t.append('sox -t 8svx - -t aiff -r 16000 -', '--') +table['8svx'] = t + +t = pipes.Template().init() +t.append('sox -t sndt - -t aiff -r 16000 -', '--') +table['sndt'] = t + +t = pipes.Template().init() +t.append('sox -t sndr - -t aiff -r 16000 -', '--') +table['sndr'] = t + +uncompress = pipes.Template().init() +uncompress.append('uncompress', '--') + + +error = 'toaiff.error' # Exception + +def toaiff(filename): + temps = [] + ret = None + try: + ret = _toaiff(filename, temps) + finally: + for temp in temps[:]: + if temp <> ret: + try: + os.unlink(temp) + except os.error: + pass + temps.remove(temp) + return ret + +def _toaiff(filename, temps): + if filename[-2:] == '.Z': + fname = tempfile.mktemp() + temps.append(fname) + sts = uncompress.copy(filename, fname) + if sts: + raise error, filename + ': uncomress failed' + else: + fname = filename + try: + ftype = sndhdr.whathdr(fname) + if ftype: + ftype = ftype[0] # All we're interested in + except IOError: + if type(msg) == type(()) and len(msg) == 2 and \ + type(msg[0]) == type(0) and type(msg[1]) == type(''): + msg = msg[1] + if type(msg) <> type(''): + msg = `msg` + raise error, filename + ': ' + msg + if ftype == 'aiff': + return fname + if ftype == None or not table.has_key(ftype): + raise error, \ + filename + ': unsupported audio file type ' + `ftype` + temp = tempfile.mktemp() + temps.append(temp) + sts = table[ftype].copy(fname, temp) + if sts: + raise error, filename + ': conversion to aiff failed' + return temp diff --git a/Lib/tzparse.py b/Lib/tzparse.py new file mode 100644 index 0000000..67c94de --- /dev/null +++ b/Lib/tzparse.py @@ -0,0 +1,76 @@ +# Parse a timezone specification. +# XXX Unfinished. +# XXX Only the typical form "XXXhhYYY;ddd/hh,ddd/hh" is currently supported. + +tzpat = '^\([A-Z][A-Z][A-Z]\)\([-+]?[0-9]+\)\([A-Z][A-Z][A-Z]\);' + \ + '\([0-9]+\)/\([0-9]+\),\([0-9]+\)/\([0-9]+\)$' + +tzprog = None + +def tzparse(tzstr): + global tzprog + if tzprog == None: + import regex + tzprog = regex.compile(tzpat) + if not tzprog.match(tzstr): + raise ValueError, 'not the TZ syntax I understand' + regs = tzprog.regs + subs = [] + for i in range(1, 8): + a, b = regs[i] + subs.append(tzstr[a:b]) + for i in (1, 3, 4, 5, 6): + subs[i] = eval(subs[i]) + [tzname, delta, dstname, daystart, hourstart, dayend, hourend] = subs + return (tzname, delta, dstname, daystart, hourstart, dayend, hourend) + +def tzlocaltime(time, params): + import calendar + (tzname, delta, dstname, daystart, hourstart, dayend, hourend) = params + year, month, days, hours, mins, secs, yday, wday = \ + calendar.gmtime(time - delta*3600) + if (daystart, hourstart) <= (yday+1, hours) < (dayend, hourend): + tzname = dstname + hours = hours + 1 + return year, month, days, hours, mins, secs, yday, wday, tzname + +def tzset(): + global tzparams, timezone, altzone, daylight, tzname + import os + tzstr = os.environ['TZ'] + tzparams = tzparse(tzstr) + timezone = tzparams[1] * 3600 + altzone = timezone + 3600 + daylight = 1 + tzname = tzparams[0], tzparams[2] + +def isdst(time): + import calendar + (tzname, delta, dstname, daystart, hourstart, dayend, hourend) = \ + tzparams + year, month, days, hours, mins, secs, yday, wday = \ + calendar.gmtime(time - delta*3600) + return (daystart, hourstart) <= (yday+1, hours) < (dayend, hourend) + +tzset() + +def localtime(time): + return tzlocaltime(time, tzparams) + +def test(): + from calendar import asctime, gmtime + import time, sys + now = time.time() + x = localtime(now) + print 'now =', now, '=', asctime(x[:-1]), x[-1] + now = now - now % (24*3600) + if sys.argv[1:]: now = now + eval(sys.argv[1]) + x = gmtime(now) + print 'gmtime =', now, '=', asctime(x), 'yday =', x[-2] + jan1 = now - x[-2]*24*3600 + x = localtime(jan1) + print 'jan1 =', jan1, '=', asctime(x[:-1]), x[-1] + for d in range(85, 95) + range(265, 275): + t = jan1 + d*24*3600 + x = localtime(t) + print 'd =', d, 't =', t, '=', asctime(x[:-1]), x[-1] diff --git a/Lib/whrandom.py b/Lib/whrandom.py index 2ce5f8f..6623904 100644 --- a/Lib/whrandom.py +++ b/Lib/whrandom.py @@ -18,57 +18,88 @@ # whrandom.random() yields double precision random numbers # uniformly distributed between 0 and 1. # -# whrandom.seed() must be called before whrandom.random() +# whrandom.seed(x, y, z) must be called before whrandom.random() # to seed the generator +# +# There is also an interface to create multiple independent +# random generators, and to choose from other ranges. # Translated by Guido van Rossum from C source provided by # Adrian Baddeley. -# The seed -# -_seed = [0, 0, 0] - - -# Set the seed -# -def seed(x, y, z): - _seed[:] = [x, y, z] - - -# Return the next random number in the range [0.0 .. 1.0) -# -def random(): - from math import floor # floor() function +class whrandom: + # + # Initialize an instance. + # Without arguments, initialize from current time. + # With arguments (x, y, z), initialize from them. # - [x, y, z] = _seed - x = 171 * (x % 177) - 2 * (x/177) - y = 172 * (y % 176) - 35 * (y/176) - z = 170 * (z % 178) - 63 * (z/178) + def init(self, *xyz): + if not xyz: + # Initialize from current time + import time + t = time.time() + t, x = divmod(t, 256) + t, y = divmod(t, 256) + t, z = divmod(t, 256) + else: + # Initialize from arguments (x, y, z) + x, y, z = xyz + self.seed(x, y, z) + return self # - if x < 0: x = x + 30269 - if y < 0: y = y + 30307 - if z < 0: z = z + 30323 + # Set the seed from (x, y, z). + # These must be integers in the range [0, 256). # - _seed[:] = [x, y, z] + def seed(self, *xyz): + if type(xyz) <> type(()) or len(xyz) <> 3: + raise TypeError, '3 seeds required' + x, y, z = xyz + if not type(x) == type(y) == type(z) == type(0): + raise TypeError, 'seeds must be integers' + if not 0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256: + raise ValueError, 'seeds must be in range(0, 256)' + self._seed = xyz # - term = float(x)/30269.0 + float(y)/30307.0 + float(z)/30323.0 - rand = term - floor(term) + # Get the next random number in the range [0.0, 1.0). # - if rand >= 1.0: rand = 0.0 # floor() inaccuracy? + def random(self): + x, y, z = self._seed + # + x1, x2 = divmod(x, 177) + y1, y2 = divmod(y, 176) + z1, z2 = divmod(z, 178) + # + x = (171 * x2 - 2 * x1) % 30269 + y = (172 * y2 - 35 * y1) % 30307 + z = (170 * z2 - 63 * z1) % 30323 + # + self._seed = x, y, z + # + return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0 # - return rand + # Get a random number in the range [a, b). + # + def uniform(self, a, b): + return a + (b-a) * self.random() + # + # Get a random integer in the range [a, b] including both end points. + # + def randint(self, a, b): + return a + int(self.random() * (b+1-a)) + # + # Choose a random element from a non-empty sequence. + # + def choice(self, seq): + return seq[int(self.random() * len(seq))] # Initialize from the current time # -def init(): - import time - t = time.time() - seed(t%256, t/256%256, t/65536%256) - - -# Make sure the generator is preset to a nonzero value -# -init() +_inst = whrandom().init() +seed = _inst.seed +random = _inst.random +uniform = _inst.uniform +randint = _inst.randint +choice = _inst.choice |