Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/calendar.py: 2%

401 statements  

« prev     ^ index     » next       coverage.py v7.0.5, created at 2023-01-17 06:13 +0000

1"""Calendar printing functions 

2 

3Note when comparing these calendars to the ones printed by cal(1): By 

4default, these calendars have Monday as the first day of the week, and 

5Sunday as the last (the European convention). Use setfirstweekday() to 

6set the first day of the week (0=Monday, 6=Sunday).""" 

7 

8import sys 

9import datetime 

10import locale as _locale 

11from itertools import repeat 

12 

13__all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday", 

14 "firstweekday", "isleap", "leapdays", "weekday", "monthrange", 

15 "monthcalendar", "prmonth", "month", "prcal", "calendar", 

16 "timegm", "month_name", "month_abbr", "day_name", "day_abbr", 

17 "Calendar", "TextCalendar", "HTMLCalendar", "LocaleTextCalendar", 

18 "LocaleHTMLCalendar", "weekheader"] 

19 

20# Exception raised for bad input (with string parameter for details) 

21error = ValueError 

22 

23# Exceptions raised for bad input 

24class IllegalMonthError(ValueError): 

25 def __init__(self, month): 

26 self.month = month 

27 def __str__(self): 

28 return "bad month number %r; must be 1-12" % self.month 

29 

30 

31class IllegalWeekdayError(ValueError): 

32 def __init__(self, weekday): 

33 self.weekday = weekday 

34 def __str__(self): 

35 return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday 

36 

37 

38# Constants for months referenced later 

39January = 1 

40February = 2 

41 

42# Number of days per month (except for February in leap years) 

43mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 

44 

45# This module used to have hard-coded lists of day and month names, as 

46# English strings. The classes following emulate a read-only version of 

47# that, but supply localized names. Note that the values are computed 

48# fresh on each call, in case the user changes locale between calls. 

49 

50class _localized_month: 

51 

52 _months = [datetime.date(2001, i+1, 1).strftime for i in range(12)] 

53 _months.insert(0, lambda x: "") 

54 

55 def __init__(self, format): 

56 self.format = format 

57 

58 def __getitem__(self, i): 

59 funcs = self._months[i] 

60 if isinstance(i, slice): 

61 return [f(self.format) for f in funcs] 

62 else: 

63 return funcs(self.format) 

64 

65 def __len__(self): 

66 return 13 

67 

68 

69class _localized_day: 

70 

71 # January 1, 2001, was a Monday. 

72 _days = [datetime.date(2001, 1, i+1).strftime for i in range(7)] 

73 

74 def __init__(self, format): 

75 self.format = format 

76 

77 def __getitem__(self, i): 

78 funcs = self._days[i] 

79 if isinstance(i, slice): 

80 return [f(self.format) for f in funcs] 

81 else: 

82 return funcs(self.format) 

83 

84 def __len__(self): 

85 return 7 

86 

87 

88# Full and abbreviated names of weekdays 

89day_name = _localized_day('%A') 

90day_abbr = _localized_day('%a') 

91 

92# Full and abbreviated names of months (1-based arrays!!!) 

93month_name = _localized_month('%B') 

94month_abbr = _localized_month('%b') 

95 

96# Constants for weekdays 

97(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) 

98 

99 

100def isleap(year): 

101 """Return True for leap years, False for non-leap years.""" 

102 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) 

103 

104 

105def leapdays(y1, y2): 

106 """Return number of leap years in range [y1, y2). 

107 Assume y1 <= y2.""" 

108 y1 -= 1 

109 y2 -= 1 

110 return (y2//4 - y1//4) - (y2//100 - y1//100) + (y2//400 - y1//400) 

111 

112 

113def weekday(year, month, day): 

114 """Return weekday (0-6 ~ Mon-Sun) for year, month (1-12), day (1-31).""" 

115 if not datetime.MINYEAR <= year <= datetime.MAXYEAR: 

116 year = 2000 + year % 400 

117 return datetime.date(year, month, day).weekday() 

118 

119 

120def monthrange(year, month): 

121 """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for 

122 year, month.""" 

123 if not 1 <= month <= 12: 

124 raise IllegalMonthError(month) 

125 day1 = weekday(year, month, 1) 

126 ndays = mdays[month] + (month == February and isleap(year)) 

127 return day1, ndays 

128 

129 

130def _monthlen(year, month): 

131 return mdays[month] + (month == February and isleap(year)) 

132 

133 

134def _prevmonth(year, month): 

135 if month == 1: 

136 return year-1, 12 

137 else: 

138 return year, month-1 

139 

140 

141def _nextmonth(year, month): 

142 if month == 12: 

143 return year+1, 1 

144 else: 

145 return year, month+1 

146 

147 

148class Calendar(object): 

149 """ 

150 Base calendar class. This class doesn't do any formatting. It simply 

151 provides data to subclasses. 

152 """ 

153 

154 def __init__(self, firstweekday=0): 

155 self.firstweekday = firstweekday # 0 = Monday, 6 = Sunday 

156 

157 def getfirstweekday(self): 

158 return self._firstweekday % 7 

159 

160 def setfirstweekday(self, firstweekday): 

161 self._firstweekday = firstweekday 

162 

163 firstweekday = property(getfirstweekday, setfirstweekday) 

164 

165 def iterweekdays(self): 

166 """ 

167 Return an iterator for one week of weekday numbers starting with the 

168 configured first one. 

169 """ 

170 for i in range(self.firstweekday, self.firstweekday + 7): 

171 yield i%7 

172 

173 def itermonthdates(self, year, month): 

174 """ 

175 Return an iterator for one month. The iterator will yield datetime.date 

176 values and will always iterate through complete weeks, so it will yield 

177 dates outside the specified month. 

178 """ 

179 for y, m, d in self.itermonthdays3(year, month): 

180 yield datetime.date(y, m, d) 

181 

182 def itermonthdays(self, year, month): 

183 """ 

184 Like itermonthdates(), but will yield day numbers. For days outside 

185 the specified month the day number is 0. 

186 """ 

187 day1, ndays = monthrange(year, month) 

188 days_before = (day1 - self.firstweekday) % 7 

189 yield from repeat(0, days_before) 

190 yield from range(1, ndays + 1) 

191 days_after = (self.firstweekday - day1 - ndays) % 7 

192 yield from repeat(0, days_after) 

193 

194 def itermonthdays2(self, year, month): 

195 """ 

196 Like itermonthdates(), but will yield (day number, weekday number) 

197 tuples. For days outside the specified month the day number is 0. 

198 """ 

199 for i, d in enumerate(self.itermonthdays(year, month), self.firstweekday): 

200 yield d, i % 7 

201 

202 def itermonthdays3(self, year, month): 

203 """ 

204 Like itermonthdates(), but will yield (year, month, day) tuples. Can be 

205 used for dates outside of datetime.date range. 

206 """ 

207 day1, ndays = monthrange(year, month) 

208 days_before = (day1 - self.firstweekday) % 7 

209 days_after = (self.firstweekday - day1 - ndays) % 7 

210 y, m = _prevmonth(year, month) 

211 end = _monthlen(y, m) + 1 

212 for d in range(end-days_before, end): 

213 yield y, m, d 

214 for d in range(1, ndays + 1): 

215 yield year, month, d 

216 y, m = _nextmonth(year, month) 

217 for d in range(1, days_after + 1): 

218 yield y, m, d 

219 

220 def itermonthdays4(self, year, month): 

221 """ 

222 Like itermonthdates(), but will yield (year, month, day, day_of_week) tuples. 

223 Can be used for dates outside of datetime.date range. 

224 """ 

225 for i, (y, m, d) in enumerate(self.itermonthdays3(year, month)): 

226 yield y, m, d, (self.firstweekday + i) % 7 

227 

228 def monthdatescalendar(self, year, month): 

229 """ 

230 Return a matrix (list of lists) representing a month's calendar. 

231 Each row represents a week; week entries are datetime.date values. 

232 """ 

233 dates = list(self.itermonthdates(year, month)) 

234 return [ dates[i:i+7] for i in range(0, len(dates), 7) ] 

235 

236 def monthdays2calendar(self, year, month): 

237 """ 

238 Return a matrix representing a month's calendar. 

239 Each row represents a week; week entries are 

240 (day number, weekday number) tuples. Day numbers outside this month 

241 are zero. 

242 """ 

243 days = list(self.itermonthdays2(year, month)) 

244 return [ days[i:i+7] for i in range(0, len(days), 7) ] 

245 

246 def monthdayscalendar(self, year, month): 

247 """ 

248 Return a matrix representing a month's calendar. 

249 Each row represents a week; days outside this month are zero. 

250 """ 

251 days = list(self.itermonthdays(year, month)) 

252 return [ days[i:i+7] for i in range(0, len(days), 7) ] 

253 

254 def yeardatescalendar(self, year, width=3): 

255 """ 

256 Return the data for the specified year ready for formatting. The return 

257 value is a list of month rows. Each month row contains up to width months. 

258 Each month contains between 4 and 6 weeks and each week contains 1-7 

259 days. Days are datetime.date objects. 

260 """ 

261 months = [ 

262 self.monthdatescalendar(year, i) 

263 for i in range(January, January+12) 

264 ] 

265 return [months[i:i+width] for i in range(0, len(months), width) ] 

266 

267 def yeardays2calendar(self, year, width=3): 

268 """ 

269 Return the data for the specified year ready for formatting (similar to 

270 yeardatescalendar()). Entries in the week lists are 

271 (day number, weekday number) tuples. Day numbers outside this month are 

272 zero. 

273 """ 

274 months = [ 

275 self.monthdays2calendar(year, i) 

276 for i in range(January, January+12) 

277 ] 

278 return [months[i:i+width] for i in range(0, len(months), width) ] 

279 

280 def yeardayscalendar(self, year, width=3): 

281 """ 

282 Return the data for the specified year ready for formatting (similar to 

283 yeardatescalendar()). Entries in the week lists are day numbers. 

284 Day numbers outside this month are zero. 

285 """ 

286 months = [ 

287 self.monthdayscalendar(year, i) 

288 for i in range(January, January+12) 

289 ] 

290 return [months[i:i+width] for i in range(0, len(months), width) ] 

291 

292 

293class TextCalendar(Calendar): 

294 """ 

295 Subclass of Calendar that outputs a calendar as a simple plain text 

296 similar to the UNIX program cal. 

297 """ 

298 

299 def prweek(self, theweek, width): 

300 """ 

301 Print a single week (no newline). 

302 """ 

303 print(self.formatweek(theweek, width), end='') 

304 

305 def formatday(self, day, weekday, width): 

306 """ 

307 Returns a formatted day. 

308 """ 

309 if day == 0: 

310 s = '' 

311 else: 

312 s = '%2i' % day # right-align single-digit days 

313 return s.center(width) 

314 

315 def formatweek(self, theweek, width): 

316 """ 

317 Returns a single week in a string (no newline). 

318 """ 

319 return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek) 

320 

321 def formatweekday(self, day, width): 

322 """ 

323 Returns a formatted week day name. 

324 """ 

325 if width >= 9: 

326 names = day_name 

327 else: 

328 names = day_abbr 

329 return names[day][:width].center(width) 

330 

331 def formatweekheader(self, width): 

332 """ 

333 Return a header for a week. 

334 """ 

335 return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays()) 

336 

337 def formatmonthname(self, theyear, themonth, width, withyear=True): 

338 """ 

339 Return a formatted month name. 

340 """ 

341 s = month_name[themonth] 

342 if withyear: 

343 s = "%s %r" % (s, theyear) 

344 return s.center(width) 

345 

346 def prmonth(self, theyear, themonth, w=0, l=0): 

347 """ 

348 Print a month's calendar. 

349 """ 

350 print(self.formatmonth(theyear, themonth, w, l), end='') 

351 

352 def formatmonth(self, theyear, themonth, w=0, l=0): 

353 """ 

354 Return a month's calendar string (multi-line). 

355 """ 

356 w = max(2, w) 

357 l = max(1, l) 

358 s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1) 

359 s = s.rstrip() 

360 s += '\n' * l 

361 s += self.formatweekheader(w).rstrip() 

362 s += '\n' * l 

363 for week in self.monthdays2calendar(theyear, themonth): 

364 s += self.formatweek(week, w).rstrip() 

365 s += '\n' * l 

366 return s 

367 

368 def formatyear(self, theyear, w=2, l=1, c=6, m=3): 

369 """ 

370 Returns a year's calendar as a multi-line string. 

371 """ 

372 w = max(2, w) 

373 l = max(1, l) 

374 c = max(2, c) 

375 colwidth = (w + 1) * 7 - 1 

376 v = [] 

377 a = v.append 

378 a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip()) 

379 a('\n'*l) 

380 header = self.formatweekheader(w) 

381 for (i, row) in enumerate(self.yeardays2calendar(theyear, m)): 

382 # months in this row 

383 months = range(m*i+1, min(m*(i+1)+1, 13)) 

384 a('\n'*l) 

385 names = (self.formatmonthname(theyear, k, colwidth, False) 

386 for k in months) 

387 a(formatstring(names, colwidth, c).rstrip()) 

388 a('\n'*l) 

389 headers = (header for k in months) 

390 a(formatstring(headers, colwidth, c).rstrip()) 

391 a('\n'*l) 

392 # max number of weeks for this row 

393 height = max(len(cal) for cal in row) 

394 for j in range(height): 

395 weeks = [] 

396 for cal in row: 

397 if j >= len(cal): 

398 weeks.append('') 

399 else: 

400 weeks.append(self.formatweek(cal[j], w)) 

401 a(formatstring(weeks, colwidth, c).rstrip()) 

402 a('\n' * l) 

403 return ''.join(v) 

404 

405 def pryear(self, theyear, w=0, l=0, c=6, m=3): 

406 """Print a year's calendar.""" 

407 print(self.formatyear(theyear, w, l, c, m), end='') 

408 

409 

410class HTMLCalendar(Calendar): 

411 """ 

412 This calendar returns complete HTML pages. 

413 """ 

414 

415 # CSS classes for the day <td>s 

416 cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] 

417 

418 # CSS classes for the day <th>s 

419 cssclasses_weekday_head = cssclasses 

420 

421 # CSS class for the days before and after current month 

422 cssclass_noday = "noday" 

423 

424 # CSS class for the month's head 

425 cssclass_month_head = "month" 

426 

427 # CSS class for the month 

428 cssclass_month = "month" 

429 

430 # CSS class for the year's table head 

431 cssclass_year_head = "year" 

432 

433 # CSS class for the whole year table 

434 cssclass_year = "year" 

435 

436 def formatday(self, day, weekday): 

437 """ 

438 Return a day as a table cell. 

439 """ 

440 if day == 0: 

441 # day outside month 

442 return '<td class="%s">&nbsp;</td>' % self.cssclass_noday 

443 else: 

444 return '<td class="%s">%d</td>' % (self.cssclasses[weekday], day) 

445 

446 def formatweek(self, theweek): 

447 """ 

448 Return a complete week as a table row. 

449 """ 

450 s = ''.join(self.formatday(d, wd) for (d, wd) in theweek) 

451 return '<tr>%s</tr>' % s 

452 

453 def formatweekday(self, day): 

454 """ 

455 Return a weekday name as a table header. 

456 """ 

457 return '<th class="%s">%s</th>' % ( 

458 self.cssclasses_weekday_head[day], day_abbr[day]) 

459 

460 def formatweekheader(self): 

461 """ 

462 Return a header for a week as a table row. 

463 """ 

464 s = ''.join(self.formatweekday(i) for i in self.iterweekdays()) 

465 return '<tr>%s</tr>' % s 

466 

467 def formatmonthname(self, theyear, themonth, withyear=True): 

468 """ 

469 Return a month name as a table row. 

470 """ 

471 if withyear: 

472 s = '%s %s' % (month_name[themonth], theyear) 

473 else: 

474 s = '%s' % month_name[themonth] 

475 return '<tr><th colspan="7" class="%s">%s</th></tr>' % ( 

476 self.cssclass_month_head, s) 

477 

478 def formatmonth(self, theyear, themonth, withyear=True): 

479 """ 

480 Return a formatted month as a table. 

481 """ 

482 v = [] 

483 a = v.append 

484 a('<table border="0" cellpadding="0" cellspacing="0" class="%s">' % ( 

485 self.cssclass_month)) 

486 a('\n') 

487 a(self.formatmonthname(theyear, themonth, withyear=withyear)) 

488 a('\n') 

489 a(self.formatweekheader()) 

490 a('\n') 

491 for week in self.monthdays2calendar(theyear, themonth): 

492 a(self.formatweek(week)) 

493 a('\n') 

494 a('</table>') 

495 a('\n') 

496 return ''.join(v) 

497 

498 def formatyear(self, theyear, width=3): 

499 """ 

500 Return a formatted year as a table of tables. 

501 """ 

502 v = [] 

503 a = v.append 

504 width = max(width, 1) 

505 a('<table border="0" cellpadding="0" cellspacing="0" class="%s">' % 

506 self.cssclass_year) 

507 a('\n') 

508 a('<tr><th colspan="%d" class="%s">%s</th></tr>' % ( 

509 width, self.cssclass_year_head, theyear)) 

510 for i in range(January, January+12, width): 

511 # months in this row 

512 months = range(i, min(i+width, 13)) 

513 a('<tr>') 

514 for m in months: 

515 a('<td>') 

516 a(self.formatmonth(theyear, m, withyear=False)) 

517 a('</td>') 

518 a('</tr>') 

519 a('</table>') 

520 return ''.join(v) 

521 

522 def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): 

523 """ 

524 Return a formatted year as a complete HTML page. 

525 """ 

526 if encoding is None: 

527 encoding = sys.getdefaultencoding() 

528 v = [] 

529 a = v.append 

530 a('<?xml version="1.0" encoding="%s"?>\n' % encoding) 

531 a('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n') 

532 a('<html>\n') 

533 a('<head>\n') 

534 a('<meta http-equiv="Content-Type" content="text/html; charset=%s" />\n' % encoding) 

535 if css is not None: 

536 a('<link rel="stylesheet" type="text/css" href="%s" />\n' % css) 

537 a('<title>Calendar for %d</title>\n' % theyear) 

538 a('</head>\n') 

539 a('<body>\n') 

540 a(self.formatyear(theyear, width)) 

541 a('</body>\n') 

542 a('</html>\n') 

543 return ''.join(v).encode(encoding, "xmlcharrefreplace") 

544 

545 

546class different_locale: 

547 def __init__(self, locale): 

548 self.locale = locale 

549 

550 def __enter__(self): 

551 self.oldlocale = _locale.getlocale(_locale.LC_TIME) 

552 _locale.setlocale(_locale.LC_TIME, self.locale) 

553 

554 def __exit__(self, *args): 

555 _locale.setlocale(_locale.LC_TIME, self.oldlocale) 

556 

557 

558class LocaleTextCalendar(TextCalendar): 

559 """ 

560 This class can be passed a locale name in the constructor and will return 

561 month and weekday names in the specified locale. If this locale includes 

562 an encoding all strings containing month and weekday names will be returned 

563 as unicode. 

564 """ 

565 

566 def __init__(self, firstweekday=0, locale=None): 

567 TextCalendar.__init__(self, firstweekday) 

568 if locale is None: 

569 locale = _locale.getdefaultlocale() 

570 self.locale = locale 

571 

572 def formatweekday(self, day, width): 

573 with different_locale(self.locale): 

574 if width >= 9: 

575 names = day_name 

576 else: 

577 names = day_abbr 

578 name = names[day] 

579 return name[:width].center(width) 

580 

581 def formatmonthname(self, theyear, themonth, width, withyear=True): 

582 with different_locale(self.locale): 

583 s = month_name[themonth] 

584 if withyear: 

585 s = "%s %r" % (s, theyear) 

586 return s.center(width) 

587 

588 

589class LocaleHTMLCalendar(HTMLCalendar): 

590 """ 

591 This class can be passed a locale name in the constructor and will return 

592 month and weekday names in the specified locale. If this locale includes 

593 an encoding all strings containing month and weekday names will be returned 

594 as unicode. 

595 """ 

596 def __init__(self, firstweekday=0, locale=None): 

597 HTMLCalendar.__init__(self, firstweekday) 

598 if locale is None: 

599 locale = _locale.getdefaultlocale() 

600 self.locale = locale 

601 

602 def formatweekday(self, day): 

603 with different_locale(self.locale): 

604 s = day_abbr[day] 

605 return '<th class="%s">%s</th>' % (self.cssclasses[day], s) 

606 

607 def formatmonthname(self, theyear, themonth, withyear=True): 

608 with different_locale(self.locale): 

609 s = month_name[themonth] 

610 if withyear: 

611 s = '%s %s' % (s, theyear) 

612 return '<tr><th colspan="7" class="month">%s</th></tr>' % s 

613 

614 

615# Support for old module level interface 

616c = TextCalendar() 

617 

618firstweekday = c.getfirstweekday 

619 

620def setfirstweekday(firstweekday): 

621 if not MONDAY <= firstweekday <= SUNDAY: 

622 raise IllegalWeekdayError(firstweekday) 

623 c.firstweekday = firstweekday 

624 

625monthcalendar = c.monthdayscalendar 

626prweek = c.prweek 

627week = c.formatweek 

628weekheader = c.formatweekheader 

629prmonth = c.prmonth 

630month = c.formatmonth 

631calendar = c.formatyear 

632prcal = c.pryear 

633 

634 

635# Spacing of month columns for multi-column year calendar 

636_colwidth = 7*3 - 1 # Amount printed by prweek() 

637_spacing = 6 # Number of spaces between columns 

638 

639 

640def format(cols, colwidth=_colwidth, spacing=_spacing): 

641 """Prints multi-column formatting for year calendars""" 

642 print(formatstring(cols, colwidth, spacing)) 

643 

644 

645def formatstring(cols, colwidth=_colwidth, spacing=_spacing): 

646 """Returns a string formatted from n strings, centered within n columns.""" 

647 spacing *= ' ' 

648 return spacing.join(c.center(colwidth) for c in cols) 

649 

650 

651EPOCH = 1970 

652_EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal() 

653 

654 

655def timegm(tuple): 

656 """Unrelated but handy function to calculate Unix timestamp from GMT.""" 

657 year, month, day, hour, minute, second = tuple[:6] 

658 days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1 

659 hours = days*24 + hour 

660 minutes = hours*60 + minute 

661 seconds = minutes*60 + second 

662 return seconds 

663 

664 

665def main(args): 

666 import argparse 

667 parser = argparse.ArgumentParser() 

668 textgroup = parser.add_argument_group('text only arguments') 

669 htmlgroup = parser.add_argument_group('html only arguments') 

670 textgroup.add_argument( 

671 "-w", "--width", 

672 type=int, default=2, 

673 help="width of date column (default 2)" 

674 ) 

675 textgroup.add_argument( 

676 "-l", "--lines", 

677 type=int, default=1, 

678 help="number of lines for each week (default 1)" 

679 ) 

680 textgroup.add_argument( 

681 "-s", "--spacing", 

682 type=int, default=6, 

683 help="spacing between months (default 6)" 

684 ) 

685 textgroup.add_argument( 

686 "-m", "--months", 

687 type=int, default=3, 

688 help="months per row (default 3)" 

689 ) 

690 htmlgroup.add_argument( 

691 "-c", "--css", 

692 default="calendar.css", 

693 help="CSS to use for page" 

694 ) 

695 parser.add_argument( 

696 "-L", "--locale", 

697 default=None, 

698 help="locale to be used from month and weekday names" 

699 ) 

700 parser.add_argument( 

701 "-e", "--encoding", 

702 default=None, 

703 help="encoding to use for output" 

704 ) 

705 parser.add_argument( 

706 "-t", "--type", 

707 default="text", 

708 choices=("text", "html"), 

709 help="output type (text or html)" 

710 ) 

711 parser.add_argument( 

712 "year", 

713 nargs='?', type=int, 

714 help="year number (1-9999)" 

715 ) 

716 parser.add_argument( 

717 "month", 

718 nargs='?', type=int, 

719 help="month number (1-12, text only)" 

720 ) 

721 

722 options = parser.parse_args(args[1:]) 

723 

724 if options.locale and not options.encoding: 

725 parser.error("if --locale is specified --encoding is required") 

726 sys.exit(1) 

727 

728 locale = options.locale, options.encoding 

729 

730 if options.type == "html": 

731 if options.locale: 

732 cal = LocaleHTMLCalendar(locale=locale) 

733 else: 

734 cal = HTMLCalendar() 

735 encoding = options.encoding 

736 if encoding is None: 

737 encoding = sys.getdefaultencoding() 

738 optdict = dict(encoding=encoding, css=options.css) 

739 write = sys.stdout.buffer.write 

740 if options.year is None: 

741 write(cal.formatyearpage(datetime.date.today().year, **optdict)) 

742 elif options.month is None: 

743 write(cal.formatyearpage(options.year, **optdict)) 

744 else: 

745 parser.error("incorrect number of arguments") 

746 sys.exit(1) 

747 else: 

748 if options.locale: 

749 cal = LocaleTextCalendar(locale=locale) 

750 else: 

751 cal = TextCalendar() 

752 optdict = dict(w=options.width, l=options.lines) 

753 if options.month is None: 

754 optdict["c"] = options.spacing 

755 optdict["m"] = options.months 

756 if options.year is None: 

757 result = cal.formatyear(datetime.date.today().year, **optdict) 

758 elif options.month is None: 

759 result = cal.formatyear(options.year, **optdict) 

760 else: 

761 result = cal.formatmonth(options.year, options.month, **optdict) 

762 write = sys.stdout.write 

763 if options.encoding: 

764 result = result.encode(options.encoding) 

765 write = sys.stdout.buffer.write 

766 write(result) 

767 

768 

769if __name__ == "__main__": 

770 main(sys.argv)