Skip to content

Commit c636014

Browse files
committed
Initial revision
1 parent df79a1e commit c636014

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+5492
-0
lines changed

Lib/calendar.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# module calendar
2+
3+
##############################
4+
# Calendar support functions #
5+
##############################
6+
7+
# This is based on UNIX ctime() et al. (also Standard C and POSIX)
8+
# Subtle but crucial differences:
9+
# - the order of the elements of a 'struct tm' differs, to ease sorting
10+
# - months numbers are 1-12, not 0-11; month arrays have a dummy element 0
11+
# - Monday is the first day of the week (numbered 0)
12+
13+
# These are really parameters of the 'time' module:
14+
epoch = 1970 # Time began on January 1 of this year (00:00:00 UCT)
15+
day_0 = 3 # The epoch begins on a Thursday (Monday = 0)
16+
17+
# Return 1 for leap years, 0 for non-leap years
18+
def isleap(year):
19+
return year % 4 = 0 and (year % 100 <> 0 or year % 400 = 0)
20+
21+
# Constants for months referenced later
22+
January = 1
23+
February = 2
24+
25+
# Number of days per month (except for February in leap years)
26+
mdays = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
27+
28+
# Exception raised for bad input (with string parameter for details)
29+
error = 'calendar error'
30+
31+
# Turn seconds since epoch into calendar time
32+
def gmtime(secs):
33+
if secs < 0: raise error, 'negative input to gmtime()'
34+
mins, secs = divmod(secs, 60)
35+
hours, mins = divmod(mins, 60)
36+
days, hours = divmod(hours, 24)
37+
wday = (days + day_0) % 7
38+
year = epoch
39+
# XXX Most of the following loop can be replaced by one division
40+
while 1:
41+
yd = 365 + isleap(year)
42+
if days < yd: break
43+
days = days - yd
44+
year = year + 1
45+
yday = days
46+
month = January
47+
while 1:
48+
md = mdays[month] + (month = February and isleap(year))
49+
if days < md: break
50+
days = days - md
51+
month = month + 1
52+
return year, month, days + 1, hours, mins, secs, yday, wday
53+
# XXX Week number also?
54+
55+
# Return number of leap years in range [y1, y2)
56+
# Assume y1 <= y2 and no funny (non-leap century) years
57+
def leapdays(y1, y2):
58+
return (y2+3)/4 - (y1+3)/4
59+
60+
# Inverse of gmtime():
61+
# Turn UCT calendar time (less yday, wday) into seconds since epoch
62+
def mktime(year, month, day, hours, mins, secs):
63+
days = day - 1
64+
for m in range(January, month): days = days + mdays[m]
65+
if isleap(year) and month > February: days = days+1
66+
days = days + (year-epoch)*365 + leapdays(epoch, year)
67+
return ((days*24 + hours)*60 + mins)*60 + secs
68+
69+
# Full and abbreviated names of weekdays
70+
day_name = ('Monday', 'Tuesday', 'Wednesday', 'Thursday')
71+
day_name = day_name + ('Friday', 'Saturday', 'Sunday')
72+
day_abbr = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
73+
74+
# Full and abbreviated of months (1-based arrays!!!)
75+
month_name = ('', 'January', 'February', 'March', 'April')
76+
month_name = month_name + ('May', 'June', 'July', 'August')
77+
month_name = month_name + ('September', 'October', 'November', 'December')
78+
month_abbr = (' ', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun')
79+
month_abbr = month_abbr + ('Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
80+
81+
# Zero-fill string to two positions (helper for asctime())
82+
def dd(s):
83+
while len(s) < 2: s = '0' + s
84+
return s
85+
86+
# Blank-fill string to two positions (helper for asctime())
87+
def zd(s):
88+
while len(s) < 2: s = ' ' + s
89+
return s
90+
91+
# Turn calendar time as returned by gmtime() into a string
92+
# (the yday parameter is for compatibility with gmtime())
93+
def asctime(year, month, day, hours, mins, secs, yday, wday):
94+
s = day_abbr[wday] + ' ' + month_abbr[month] + ' ' + zd(`day`)
95+
s = s + ' ' + dd(`hours`) + ':' + dd(`mins`) + ':' + dd(`secs`)
96+
return s + ' ' + `year`
97+
98+
# Localization: Minutes West from Greenwich
99+
# timezone = -2*60 # Middle-European time with DST on
100+
timezone = 5*60 # EST (sigh -- THINK time() doesn't return UCT)
101+
102+
# Local time ignores DST issues for now -- adjust 'timezone' to fake it
103+
def localtime(secs):
104+
return gmtime(secs - timezone*60)
105+
106+
# UNIX-style ctime (except it doesn't append '\n'!)
107+
def ctime(secs):
108+
return asctime(localtime(secs))
109+
110+
######################
111+
# Non-UNIX additions #
112+
######################
113+
114+
# Calendar printing etc.
115+
116+
# Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), day (1-31)
117+
def weekday(year, month, day):
118+
secs = mktime(year, month, day, 0, 0, 0)
119+
days = secs / (24*60*60)
120+
return (days + day_0) % 7
121+
122+
# Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for year, month
123+
def monthrange(year, month):
124+
day1 = weekday(year, month, 1)
125+
ndays = mdays[month] + (month = February and isleap(year))
126+
return day1, ndays
127+
128+
# Return a matrix representing a month's calendar
129+
# Each row represents a week; days outside this month are zero
130+
def _monthcalendar(year, month):
131+
day1, ndays = monthrange(year, month)
132+
rows = []
133+
r7 = range(7)
134+
day = 1 - day1
135+
while day <= ndays:
136+
row = [0, 0, 0, 0, 0, 0, 0]
137+
for i in r7:
138+
if 1 <= day <= ndays: row[i] = day
139+
day = day + 1
140+
rows.append(row)
141+
return rows
142+
143+
# Caching interface to _monthcalendar
144+
mc_cache = {}
145+
def monthcalendar(year, month):
146+
key = `year` + month_abbr[month]
147+
try:
148+
return mc_cache[key]
149+
except RuntimeError:
150+
mc_cache[key] = ret = _monthcalendar(year, month)
151+
return ret
152+
153+
# Center a string in a field
154+
def center(str, width):
155+
n = width - len(str)
156+
if n < 0: return str
157+
return ' '*(n/2) + str + ' '*(n-n/2)
158+
159+
# XXX The following code knows that print separates items with space!
160+
161+
# Print a single week (no newline)
162+
def prweek(week, width):
163+
for day in week:
164+
if day = 0: print ' '*width,
165+
else:
166+
if width > 2: print ' '*(width-3),
167+
if day < 10: print '',
168+
print day,
169+
170+
# Return a header for a week
171+
def weekheader(width):
172+
str = ''
173+
for i in range(7):
174+
if str: str = str + ' '
175+
str = str + day_abbr[i%7][:width]
176+
return str
177+
178+
# Print a month's calendar
179+
def prmonth(year, month):
180+
print weekheader(3)
181+
for week in monthcalendar(year, month):
182+
prweek(week, 3)
183+
print
184+
185+
# Spacing between month columns
186+
spacing = ' '
187+
188+
# 3-column formatting for year calendars
189+
def format3c(a, b, c):
190+
print center(a, 20), spacing, center(b, 20), spacing, center(c, 20)
191+
192+
# Print a year's calendar
193+
def prcal(year):
194+
header = weekheader(2)
195+
format3c('', `year`, '')
196+
for q in range(January, January+12, 3):
197+
print
198+
format3c(month_name[q], month_name[q+1], month_name[q+2])
199+
format3c(header, header, header)
200+
data = []
201+
height = 0
202+
for month in range(q, q+3):
203+
cal = monthcalendar(year, month)
204+
if len(cal) > height: height = len(cal)
205+
data.append(cal)
206+
for i in range(height):
207+
for cal in data:
208+
if i >= len(cal):
209+
print ' '*20,
210+
else:
211+
prweek(cal[i], 2)
212+
print spacing,
213+
print

Lib/cmp.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Module 'cmp'
2+
3+
# Efficiently compare files, boolean outcome only (equal / not equal).
4+
5+
# Tricks (used in this order):
6+
# - Files with identical type, size & mtime are assumed to be clones
7+
# - Files with different type or size cannot be identical
8+
# - We keep a cache of outcomes of earlier comparisons
9+
# - We don't fork a process to run 'cmp' but read the files ourselves
10+
11+
import posix
12+
13+
cache = {}
14+
15+
def cmp(f1, f2): # Compare two files, use the cache if possible.
16+
# Return 1 for identical files, 0 for different.
17+
# Raise exceptions if either file could not be statted, read, etc.
18+
s1, s2 = sig(posix.stat(f1)), sig(posix.stat(f2))
19+
if s1[0] <> 8 or s2[0] <> 8:
20+
# Either is a not a plain file -- always report as different
21+
return 0
22+
if s1 = s2:
23+
# type, size & mtime match -- report same
24+
return 1
25+
if s1[:2] <> s2[:2]: # Types or sizes differ, don't bother
26+
# types or sizes differ -- report different
27+
return 0
28+
# same type and size -- look in the cache
29+
key = f1 + ' ' + f2
30+
try:
31+
cs1, cs2, outcome = cache[key]
32+
# cache hit
33+
if s1 = cs1 and s2 = cs2:
34+
# cached signatures match
35+
return outcome
36+
# stale cached signature(s)
37+
except RuntimeError:
38+
# cache miss
39+
pass
40+
# really compare
41+
outcome = do_cmp(f1, f2)
42+
cache[key] = s1, s2, outcome
43+
return outcome
44+
45+
def sig(st): # Return signature (i.e., type, size, mtime) from raw stat data
46+
# 0-5: st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid
47+
# 6-9: st_size, st_atime, st_mtime, st_ctime
48+
type = st[0] / 4096
49+
size = st[6]
50+
mtime = st[8]
51+
return type, size, mtime
52+
53+
def do_cmp(f1, f2): # Compare two files, really
54+
bufsize = 8096 # Could be tuned
55+
fp1 = open(f1, 'r')
56+
fp2 = open(f2, 'r')
57+
while 1:
58+
b1 = fp1.read(bufsize)
59+
b2 = fp2.read(bufsize)
60+
if b1 <> b2: return 0
61+
if not b1: return 1

Lib/cmpcache.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Module 'cmpcache'
2+
#
3+
# Efficiently compare files, boolean outcome only (equal / not equal).
4+
#
5+
# Tricks (used in this order):
6+
# - Use the statcache module to avoid statting files more than once
7+
# - Files with identical type, size & mtime are assumed to be clones
8+
# - Files with different type or size cannot be identical
9+
# - We keep a cache of outcomes of earlier comparisons
10+
# - We don't fork a process to run 'cmp' but read the files ourselves
11+
#
12+
# XXX There is a dependency on constants in <sys/stat.h> here.
13+
14+
import posix
15+
import statcache
16+
17+
18+
# The cache.
19+
#
20+
cache = {}
21+
22+
23+
# Compare two files, use the cache if possible.
24+
# May raise posix.error if a stat or open of either fails.
25+
#
26+
def cmp(f1, f2):
27+
# Return 1 for identical files, 0 for different.
28+
# Raise exceptions if either file could not be statted, read, etc.
29+
s1, s2 = sig(statcache.stat(f1)), sig(statcache.stat(f2))
30+
if s1[0] <> 8 or s2[0] <> 8: # XXX 8 is S_IFREG in <sys/stat.h>
31+
# Either is a not a plain file -- always report as different
32+
return 0
33+
if s1 = s2:
34+
# type, size & mtime match -- report same
35+
return 1
36+
if s1[:2] <> s2[:2]: # Types or sizes differ, don't bother
37+
# types or sizes differ -- report different
38+
return 0
39+
# same type and size -- look in the cache
40+
key = f1 + ' ' + f2
41+
if cache.has_key(key):
42+
cs1, cs2, outcome = cache[key]
43+
# cache hit
44+
if s1 = cs1 and s2 = cs2:
45+
# cached signatures match
46+
return outcome
47+
# stale cached signature(s)
48+
# really compare
49+
outcome = do_cmp(f1, f2)
50+
cache[key] = s1, s2, outcome
51+
return outcome
52+
53+
# Return signature (i.e., type, size, mtime) from raw stat data.
54+
#
55+
def sig(st):
56+
# 0-5: st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid
57+
# 6-9: st_size, st_atime, st_mtime, st_ctime
58+
type = st[0] / 4096 # XXX dependent on S_IFMT in <sys/stat.h>
59+
size = st[6]
60+
mtime = st[8]
61+
return type, size, mtime
62+
63+
# Compare two files, really.
64+
#
65+
def do_cmp(f1, f2):
66+
#print ' cmp', f1, f2 # XXX remove when debugged
67+
bufsize = 8096 # Could be tuned
68+
fp1 = open(f1, 'r')
69+
fp2 = open(f2, 'r')
70+
while 1:
71+
b1 = fp1.read(bufsize)
72+
b2 = fp2.read(bufsize)
73+
if b1 <> b2: return 0
74+
if not b1: return 1

0 commit comments

Comments
 (0)