1import calendar 2import unittest 3 4from test import support 5from test.support.script_helper import assert_python_ok, assert_python_failure 6import contextlib 7import datetime 8import io 9import locale 10import os 11import sys 12import time 13 14# From https://en.wikipedia.org/wiki/Leap_year_starting_on_Saturday 15result_0_02_text = """\ 16 February 0 17Mo Tu We Th Fr Sa Su 18 1 2 3 4 5 6 19 7 8 9 10 11 12 13 2014 15 16 17 18 19 20 2121 22 23 24 25 26 27 2228 29 23""" 24 25result_0_text = """\ 26 0 27 28 January February March 29Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 30 1 2 1 2 3 4 5 6 1 2 3 4 5 31 3 4 5 6 7 8 9 7 8 9 10 11 12 13 6 7 8 9 10 11 12 3210 11 12 13 14 15 16 14 15 16 17 18 19 20 13 14 15 16 17 18 19 3317 18 19 20 21 22 23 21 22 23 24 25 26 27 20 21 22 23 24 25 26 3424 25 26 27 28 29 30 28 29 27 28 29 30 31 3531 36 37 April May June 38Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 39 1 2 1 2 3 4 5 6 7 1 2 3 4 40 3 4 5 6 7 8 9 8 9 10 11 12 13 14 5 6 7 8 9 10 11 4110 11 12 13 14 15 16 15 16 17 18 19 20 21 12 13 14 15 16 17 18 4217 18 19 20 21 22 23 22 23 24 25 26 27 28 19 20 21 22 23 24 25 4324 25 26 27 28 29 30 29 30 31 26 27 28 29 30 44 45 July August September 46Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 47 1 2 1 2 3 4 5 6 1 2 3 48 3 4 5 6 7 8 9 7 8 9 10 11 12 13 4 5 6 7 8 9 10 4910 11 12 13 14 15 16 14 15 16 17 18 19 20 11 12 13 14 15 16 17 5017 18 19 20 21 22 23 21 22 23 24 25 26 27 18 19 20 21 22 23 24 5124 25 26 27 28 29 30 28 29 30 31 25 26 27 28 29 30 5231 53 54 October November December 55Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 56 1 1 2 3 4 5 1 2 3 57 2 3 4 5 6 7 8 6 7 8 9 10 11 12 4 5 6 7 8 9 10 58 9 10 11 12 13 14 15 13 14 15 16 17 18 19 11 12 13 14 15 16 17 5916 17 18 19 20 21 22 20 21 22 23 24 25 26 18 19 20 21 22 23 24 6023 24 25 26 27 28 29 27 28 29 30 25 26 27 28 29 30 31 6130 31 62""" 63 64result_2004_01_text = """\ 65 January 2004 66Mo Tu We Th Fr Sa Su 67 1 2 3 4 68 5 6 7 8 9 10 11 6912 13 14 15 16 17 18 7019 20 21 22 23 24 25 7126 27 28 29 30 31 72""" 73 74result_2004_text = """\ 75 2004 76 77 January February March 78Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 79 1 2 3 4 1 1 2 3 4 5 6 7 80 5 6 7 8 9 10 11 2 3 4 5 6 7 8 8 9 10 11 12 13 14 8112 13 14 15 16 17 18 9 10 11 12 13 14 15 15 16 17 18 19 20 21 8219 20 21 22 23 24 25 16 17 18 19 20 21 22 22 23 24 25 26 27 28 8326 27 28 29 30 31 23 24 25 26 27 28 29 29 30 31 84 85 April May June 86Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 87 1 2 3 4 1 2 1 2 3 4 5 6 88 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13 8912 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20 9019 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27 9126 27 28 29 30 24 25 26 27 28 29 30 28 29 30 92 31 93 94 July August September 95Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 96 1 2 3 4 1 1 2 3 4 5 97 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12 9812 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19 9919 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26 10026 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30 101 30 31 102 103 October November December 104Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 105 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 106 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12 10711 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19 10818 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26 10925 26 27 28 29 30 31 29 30 27 28 29 30 31 110""" 111 112 113default_format = dict(year="year", month="month", encoding="ascii") 114 115result_2004_html = """\ 116<?xml version="1.0" encoding="{encoding}"?> 117<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 118<html> 119<head> 120<meta http-equiv="Content-Type" content="text/html; charset={encoding}" /> 121<link rel="stylesheet" type="text/css" href="calendar.css" /> 122<title>Calendar for 2004</title> 123</head> 124<body> 125<table border="0" cellpadding="0" cellspacing="0" class="{year}"> 126<tr><th colspan="3" class="{year}">2004</th></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 127<tr><th colspan="7" class="{month}">January</th></tr> 128<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 129<tr><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="thu">1</td><td class="fri">2</td><td class="sat">3</td><td class="sun">4</td></tr> 130<tr><td class="mon">5</td><td class="tue">6</td><td class="wed">7</td><td class="thu">8</td><td class="fri">9</td><td class="sat">10</td><td class="sun">11</td></tr> 131<tr><td class="mon">12</td><td class="tue">13</td><td class="wed">14</td><td class="thu">15</td><td class="fri">16</td><td class="sat">17</td><td class="sun">18</td></tr> 132<tr><td class="mon">19</td><td class="tue">20</td><td class="wed">21</td><td class="thu">22</td><td class="fri">23</td><td class="sat">24</td><td class="sun">25</td></tr> 133<tr><td class="mon">26</td><td class="tue">27</td><td class="wed">28</td><td class="thu">29</td><td class="fri">30</td><td class="sat">31</td><td class="noday"> </td></tr> 134</table> 135</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 136<tr><th colspan="7" class="{month}">February</th></tr> 137<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 138<tr><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="sun">1</td></tr> 139<tr><td class="mon">2</td><td class="tue">3</td><td class="wed">4</td><td class="thu">5</td><td class="fri">6</td><td class="sat">7</td><td class="sun">8</td></tr> 140<tr><td class="mon">9</td><td class="tue">10</td><td class="wed">11</td><td class="thu">12</td><td class="fri">13</td><td class="sat">14</td><td class="sun">15</td></tr> 141<tr><td class="mon">16</td><td class="tue">17</td><td class="wed">18</td><td class="thu">19</td><td class="fri">20</td><td class="sat">21</td><td class="sun">22</td></tr> 142<tr><td class="mon">23</td><td class="tue">24</td><td class="wed">25</td><td class="thu">26</td><td class="fri">27</td><td class="sat">28</td><td class="sun">29</td></tr> 143</table> 144</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 145<tr><th colspan="7" class="{month}">March</th></tr> 146<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 147<tr><td class="mon">1</td><td class="tue">2</td><td class="wed">3</td><td class="thu">4</td><td class="fri">5</td><td class="sat">6</td><td class="sun">7</td></tr> 148<tr><td class="mon">8</td><td class="tue">9</td><td class="wed">10</td><td class="thu">11</td><td class="fri">12</td><td class="sat">13</td><td class="sun">14</td></tr> 149<tr><td class="mon">15</td><td class="tue">16</td><td class="wed">17</td><td class="thu">18</td><td class="fri">19</td><td class="sat">20</td><td class="sun">21</td></tr> 150<tr><td class="mon">22</td><td class="tue">23</td><td class="wed">24</td><td class="thu">25</td><td class="fri">26</td><td class="sat">27</td><td class="sun">28</td></tr> 151<tr><td class="mon">29</td><td class="tue">30</td><td class="wed">31</td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td></tr> 152</table> 153</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 154<tr><th colspan="7" class="{month}">April</th></tr> 155<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 156<tr><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="thu">1</td><td class="fri">2</td><td class="sat">3</td><td class="sun">4</td></tr> 157<tr><td class="mon">5</td><td class="tue">6</td><td class="wed">7</td><td class="thu">8</td><td class="fri">9</td><td class="sat">10</td><td class="sun">11</td></tr> 158<tr><td class="mon">12</td><td class="tue">13</td><td class="wed">14</td><td class="thu">15</td><td class="fri">16</td><td class="sat">17</td><td class="sun">18</td></tr> 159<tr><td class="mon">19</td><td class="tue">20</td><td class="wed">21</td><td class="thu">22</td><td class="fri">23</td><td class="sat">24</td><td class="sun">25</td></tr> 160<tr><td class="mon">26</td><td class="tue">27</td><td class="wed">28</td><td class="thu">29</td><td class="fri">30</td><td class="noday"> </td><td class="noday"> </td></tr> 161</table> 162</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 163<tr><th colspan="7" class="{month}">May</th></tr> 164<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 165<tr><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="sat">1</td><td class="sun">2</td></tr> 166<tr><td class="mon">3</td><td class="tue">4</td><td class="wed">5</td><td class="thu">6</td><td class="fri">7</td><td class="sat">8</td><td class="sun">9</td></tr> 167<tr><td class="mon">10</td><td class="tue">11</td><td class="wed">12</td><td class="thu">13</td><td class="fri">14</td><td class="sat">15</td><td class="sun">16</td></tr> 168<tr><td class="mon">17</td><td class="tue">18</td><td class="wed">19</td><td class="thu">20</td><td class="fri">21</td><td class="sat">22</td><td class="sun">23</td></tr> 169<tr><td class="mon">24</td><td class="tue">25</td><td class="wed">26</td><td class="thu">27</td><td class="fri">28</td><td class="sat">29</td><td class="sun">30</td></tr> 170<tr><td class="mon">31</td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td></tr> 171</table> 172</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 173<tr><th colspan="7" class="{month}">June</th></tr> 174<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 175<tr><td class="noday"> </td><td class="tue">1</td><td class="wed">2</td><td class="thu">3</td><td class="fri">4</td><td class="sat">5</td><td class="sun">6</td></tr> 176<tr><td class="mon">7</td><td class="tue">8</td><td class="wed">9</td><td class="thu">10</td><td class="fri">11</td><td class="sat">12</td><td class="sun">13</td></tr> 177<tr><td class="mon">14</td><td class="tue">15</td><td class="wed">16</td><td class="thu">17</td><td class="fri">18</td><td class="sat">19</td><td class="sun">20</td></tr> 178<tr><td class="mon">21</td><td class="tue">22</td><td class="wed">23</td><td class="thu">24</td><td class="fri">25</td><td class="sat">26</td><td class="sun">27</td></tr> 179<tr><td class="mon">28</td><td class="tue">29</td><td class="wed">30</td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td></tr> 180</table> 181</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 182<tr><th colspan="7" class="{month}">July</th></tr> 183<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 184<tr><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="thu">1</td><td class="fri">2</td><td class="sat">3</td><td class="sun">4</td></tr> 185<tr><td class="mon">5</td><td class="tue">6</td><td class="wed">7</td><td class="thu">8</td><td class="fri">9</td><td class="sat">10</td><td class="sun">11</td></tr> 186<tr><td class="mon">12</td><td class="tue">13</td><td class="wed">14</td><td class="thu">15</td><td class="fri">16</td><td class="sat">17</td><td class="sun">18</td></tr> 187<tr><td class="mon">19</td><td class="tue">20</td><td class="wed">21</td><td class="thu">22</td><td class="fri">23</td><td class="sat">24</td><td class="sun">25</td></tr> 188<tr><td class="mon">26</td><td class="tue">27</td><td class="wed">28</td><td class="thu">29</td><td class="fri">30</td><td class="sat">31</td><td class="noday"> </td></tr> 189</table> 190</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 191<tr><th colspan="7" class="{month}">August</th></tr> 192<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 193<tr><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="sun">1</td></tr> 194<tr><td class="mon">2</td><td class="tue">3</td><td class="wed">4</td><td class="thu">5</td><td class="fri">6</td><td class="sat">7</td><td class="sun">8</td></tr> 195<tr><td class="mon">9</td><td class="tue">10</td><td class="wed">11</td><td class="thu">12</td><td class="fri">13</td><td class="sat">14</td><td class="sun">15</td></tr> 196<tr><td class="mon">16</td><td class="tue">17</td><td class="wed">18</td><td class="thu">19</td><td class="fri">20</td><td class="sat">21</td><td class="sun">22</td></tr> 197<tr><td class="mon">23</td><td class="tue">24</td><td class="wed">25</td><td class="thu">26</td><td class="fri">27</td><td class="sat">28</td><td class="sun">29</td></tr> 198<tr><td class="mon">30</td><td class="tue">31</td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td></tr> 199</table> 200</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 201<tr><th colspan="7" class="{month}">September</th></tr> 202<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 203<tr><td class="noday"> </td><td class="noday"> </td><td class="wed">1</td><td class="thu">2</td><td class="fri">3</td><td class="sat">4</td><td class="sun">5</td></tr> 204<tr><td class="mon">6</td><td class="tue">7</td><td class="wed">8</td><td class="thu">9</td><td class="fri">10</td><td class="sat">11</td><td class="sun">12</td></tr> 205<tr><td class="mon">13</td><td class="tue">14</td><td class="wed">15</td><td class="thu">16</td><td class="fri">17</td><td class="sat">18</td><td class="sun">19</td></tr> 206<tr><td class="mon">20</td><td class="tue">21</td><td class="wed">22</td><td class="thu">23</td><td class="fri">24</td><td class="sat">25</td><td class="sun">26</td></tr> 207<tr><td class="mon">27</td><td class="tue">28</td><td class="wed">29</td><td class="thu">30</td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td></tr> 208</table> 209</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 210<tr><th colspan="7" class="{month}">October</th></tr> 211<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 212<tr><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="fri">1</td><td class="sat">2</td><td class="sun">3</td></tr> 213<tr><td class="mon">4</td><td class="tue">5</td><td class="wed">6</td><td class="thu">7</td><td class="fri">8</td><td class="sat">9</td><td class="sun">10</td></tr> 214<tr><td class="mon">11</td><td class="tue">12</td><td class="wed">13</td><td class="thu">14</td><td class="fri">15</td><td class="sat">16</td><td class="sun">17</td></tr> 215<tr><td class="mon">18</td><td class="tue">19</td><td class="wed">20</td><td class="thu">21</td><td class="fri">22</td><td class="sat">23</td><td class="sun">24</td></tr> 216<tr><td class="mon">25</td><td class="tue">26</td><td class="wed">27</td><td class="thu">28</td><td class="fri">29</td><td class="sat">30</td><td class="sun">31</td></tr> 217</table> 218</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 219<tr><th colspan="7" class="{month}">November</th></tr> 220<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 221<tr><td class="mon">1</td><td class="tue">2</td><td class="wed">3</td><td class="thu">4</td><td class="fri">5</td><td class="sat">6</td><td class="sun">7</td></tr> 222<tr><td class="mon">8</td><td class="tue">9</td><td class="wed">10</td><td class="thu">11</td><td class="fri">12</td><td class="sat">13</td><td class="sun">14</td></tr> 223<tr><td class="mon">15</td><td class="tue">16</td><td class="wed">17</td><td class="thu">18</td><td class="fri">19</td><td class="sat">20</td><td class="sun">21</td></tr> 224<tr><td class="mon">22</td><td class="tue">23</td><td class="wed">24</td><td class="thu">25</td><td class="fri">26</td><td class="sat">27</td><td class="sun">28</td></tr> 225<tr><td class="mon">29</td><td class="tue">30</td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td><td class="noday"> </td></tr> 226</table> 227</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}"> 228<tr><th colspan="7" class="{month}">December</th></tr> 229<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr> 230<tr><td class="noday"> </td><td class="noday"> </td><td class="wed">1</td><td class="thu">2</td><td class="fri">3</td><td class="sat">4</td><td class="sun">5</td></tr> 231<tr><td class="mon">6</td><td class="tue">7</td><td class="wed">8</td><td class="thu">9</td><td class="fri">10</td><td class="sat">11</td><td class="sun">12</td></tr> 232<tr><td class="mon">13</td><td class="tue">14</td><td class="wed">15</td><td class="thu">16</td><td class="fri">17</td><td class="sat">18</td><td class="sun">19</td></tr> 233<tr><td class="mon">20</td><td class="tue">21</td><td class="wed">22</td><td class="thu">23</td><td class="fri">24</td><td class="sat">25</td><td class="sun">26</td></tr> 234<tr><td class="mon">27</td><td class="tue">28</td><td class="wed">29</td><td class="thu">30</td><td class="fri">31</td><td class="noday"> </td><td class="noday"> </td></tr> 235</table> 236</td></tr></table></body> 237</html> 238""" 239 240result_2004_days = [ 241 [[[0, 0, 0, 1, 2, 3, 4], 242 [5, 6, 7, 8, 9, 10, 11], 243 [12, 13, 14, 15, 16, 17, 18], 244 [19, 20, 21, 22, 23, 24, 25], 245 [26, 27, 28, 29, 30, 31, 0]], 246 [[0, 0, 0, 0, 0, 0, 1], 247 [2, 3, 4, 5, 6, 7, 8], 248 [9, 10, 11, 12, 13, 14, 15], 249 [16, 17, 18, 19, 20, 21, 22], 250 [23, 24, 25, 26, 27, 28, 29]], 251 [[1, 2, 3, 4, 5, 6, 7], 252 [8, 9, 10, 11, 12, 13, 14], 253 [15, 16, 17, 18, 19, 20, 21], 254 [22, 23, 24, 25, 26, 27, 28], 255 [29, 30, 31, 0, 0, 0, 0]]], 256 [[[0, 0, 0, 1, 2, 3, 4], 257 [5, 6, 7, 8, 9, 10, 11], 258 [12, 13, 14, 15, 16, 17, 18], 259 [19, 20, 21, 22, 23, 24, 25], 260 [26, 27, 28, 29, 30, 0, 0]], 261 [[0, 0, 0, 0, 0, 1, 2], 262 [3, 4, 5, 6, 7, 8, 9], 263 [10, 11, 12, 13, 14, 15, 16], 264 [17, 18, 19, 20, 21, 22, 23], 265 [24, 25, 26, 27, 28, 29, 30], 266 [31, 0, 0, 0, 0, 0, 0]], 267 [[0, 1, 2, 3, 4, 5, 6], 268 [7, 8, 9, 10, 11, 12, 13], 269 [14, 15, 16, 17, 18, 19, 20], 270 [21, 22, 23, 24, 25, 26, 27], 271 [28, 29, 30, 0, 0, 0, 0]]], 272 [[[0, 0, 0, 1, 2, 3, 4], 273 [5, 6, 7, 8, 9, 10, 11], 274 [12, 13, 14, 15, 16, 17, 18], 275 [19, 20, 21, 22, 23, 24, 25], 276 [26, 27, 28, 29, 30, 31, 0]], 277 [[0, 0, 0, 0, 0, 0, 1], 278 [2, 3, 4, 5, 6, 7, 8], 279 [9, 10, 11, 12, 13, 14, 15], 280 [16, 17, 18, 19, 20, 21, 22], 281 [23, 24, 25, 26, 27, 28, 29], 282 [30, 31, 0, 0, 0, 0, 0]], 283 [[0, 0, 1, 2, 3, 4, 5], 284 [6, 7, 8, 9, 10, 11, 12], 285 [13, 14, 15, 16, 17, 18, 19], 286 [20, 21, 22, 23, 24, 25, 26], 287 [27, 28, 29, 30, 0, 0, 0]]], 288 [[[0, 0, 0, 0, 1, 2, 3], 289 [4, 5, 6, 7, 8, 9, 10], 290 [11, 12, 13, 14, 15, 16, 17], 291 [18, 19, 20, 21, 22, 23, 24], 292 [25, 26, 27, 28, 29, 30, 31]], 293 [[1, 2, 3, 4, 5, 6, 7], 294 [8, 9, 10, 11, 12, 13, 14], 295 [15, 16, 17, 18, 19, 20, 21], 296 [22, 23, 24, 25, 26, 27, 28], 297 [29, 30, 0, 0, 0, 0, 0]], 298 [[0, 0, 1, 2, 3, 4, 5], 299 [6, 7, 8, 9, 10, 11, 12], 300 [13, 14, 15, 16, 17, 18, 19], 301 [20, 21, 22, 23, 24, 25, 26], 302 [27, 28, 29, 30, 31, 0, 0]]] 303] 304 305result_2004_dates = \ 306 [[['12/29/03 12/30/03 12/31/03 01/01/04 01/02/04 01/03/04 01/04/04', 307 '01/05/04 01/06/04 01/07/04 01/08/04 01/09/04 01/10/04 01/11/04', 308 '01/12/04 01/13/04 01/14/04 01/15/04 01/16/04 01/17/04 01/18/04', 309 '01/19/04 01/20/04 01/21/04 01/22/04 01/23/04 01/24/04 01/25/04', 310 '01/26/04 01/27/04 01/28/04 01/29/04 01/30/04 01/31/04 02/01/04'], 311 ['01/26/04 01/27/04 01/28/04 01/29/04 01/30/04 01/31/04 02/01/04', 312 '02/02/04 02/03/04 02/04/04 02/05/04 02/06/04 02/07/04 02/08/04', 313 '02/09/04 02/10/04 02/11/04 02/12/04 02/13/04 02/14/04 02/15/04', 314 '02/16/04 02/17/04 02/18/04 02/19/04 02/20/04 02/21/04 02/22/04', 315 '02/23/04 02/24/04 02/25/04 02/26/04 02/27/04 02/28/04 02/29/04'], 316 ['03/01/04 03/02/04 03/03/04 03/04/04 03/05/04 03/06/04 03/07/04', 317 '03/08/04 03/09/04 03/10/04 03/11/04 03/12/04 03/13/04 03/14/04', 318 '03/15/04 03/16/04 03/17/04 03/18/04 03/19/04 03/20/04 03/21/04', 319 '03/22/04 03/23/04 03/24/04 03/25/04 03/26/04 03/27/04 03/28/04', 320 '03/29/04 03/30/04 03/31/04 04/01/04 04/02/04 04/03/04 04/04/04']], 321 [['03/29/04 03/30/04 03/31/04 04/01/04 04/02/04 04/03/04 04/04/04', 322 '04/05/04 04/06/04 04/07/04 04/08/04 04/09/04 04/10/04 04/11/04', 323 '04/12/04 04/13/04 04/14/04 04/15/04 04/16/04 04/17/04 04/18/04', 324 '04/19/04 04/20/04 04/21/04 04/22/04 04/23/04 04/24/04 04/25/04', 325 '04/26/04 04/27/04 04/28/04 04/29/04 04/30/04 05/01/04 05/02/04'], 326 ['04/26/04 04/27/04 04/28/04 04/29/04 04/30/04 05/01/04 05/02/04', 327 '05/03/04 05/04/04 05/05/04 05/06/04 05/07/04 05/08/04 05/09/04', 328 '05/10/04 05/11/04 05/12/04 05/13/04 05/14/04 05/15/04 05/16/04', 329 '05/17/04 05/18/04 05/19/04 05/20/04 05/21/04 05/22/04 05/23/04', 330 '05/24/04 05/25/04 05/26/04 05/27/04 05/28/04 05/29/04 05/30/04', 331 '05/31/04 06/01/04 06/02/04 06/03/04 06/04/04 06/05/04 06/06/04'], 332 ['05/31/04 06/01/04 06/02/04 06/03/04 06/04/04 06/05/04 06/06/04', 333 '06/07/04 06/08/04 06/09/04 06/10/04 06/11/04 06/12/04 06/13/04', 334 '06/14/04 06/15/04 06/16/04 06/17/04 06/18/04 06/19/04 06/20/04', 335 '06/21/04 06/22/04 06/23/04 06/24/04 06/25/04 06/26/04 06/27/04', 336 '06/28/04 06/29/04 06/30/04 07/01/04 07/02/04 07/03/04 07/04/04']], 337 [['06/28/04 06/29/04 06/30/04 07/01/04 07/02/04 07/03/04 07/04/04', 338 '07/05/04 07/06/04 07/07/04 07/08/04 07/09/04 07/10/04 07/11/04', 339 '07/12/04 07/13/04 07/14/04 07/15/04 07/16/04 07/17/04 07/18/04', 340 '07/19/04 07/20/04 07/21/04 07/22/04 07/23/04 07/24/04 07/25/04', 341 '07/26/04 07/27/04 07/28/04 07/29/04 07/30/04 07/31/04 08/01/04'], 342 ['07/26/04 07/27/04 07/28/04 07/29/04 07/30/04 07/31/04 08/01/04', 343 '08/02/04 08/03/04 08/04/04 08/05/04 08/06/04 08/07/04 08/08/04', 344 '08/09/04 08/10/04 08/11/04 08/12/04 08/13/04 08/14/04 08/15/04', 345 '08/16/04 08/17/04 08/18/04 08/19/04 08/20/04 08/21/04 08/22/04', 346 '08/23/04 08/24/04 08/25/04 08/26/04 08/27/04 08/28/04 08/29/04', 347 '08/30/04 08/31/04 09/01/04 09/02/04 09/03/04 09/04/04 09/05/04'], 348 ['08/30/04 08/31/04 09/01/04 09/02/04 09/03/04 09/04/04 09/05/04', 349 '09/06/04 09/07/04 09/08/04 09/09/04 09/10/04 09/11/04 09/12/04', 350 '09/13/04 09/14/04 09/15/04 09/16/04 09/17/04 09/18/04 09/19/04', 351 '09/20/04 09/21/04 09/22/04 09/23/04 09/24/04 09/25/04 09/26/04', 352 '09/27/04 09/28/04 09/29/04 09/30/04 10/01/04 10/02/04 10/03/04']], 353 [['09/27/04 09/28/04 09/29/04 09/30/04 10/01/04 10/02/04 10/03/04', 354 '10/04/04 10/05/04 10/06/04 10/07/04 10/08/04 10/09/04 10/10/04', 355 '10/11/04 10/12/04 10/13/04 10/14/04 10/15/04 10/16/04 10/17/04', 356 '10/18/04 10/19/04 10/20/04 10/21/04 10/22/04 10/23/04 10/24/04', 357 '10/25/04 10/26/04 10/27/04 10/28/04 10/29/04 10/30/04 10/31/04'], 358 ['11/01/04 11/02/04 11/03/04 11/04/04 11/05/04 11/06/04 11/07/04', 359 '11/08/04 11/09/04 11/10/04 11/11/04 11/12/04 11/13/04 11/14/04', 360 '11/15/04 11/16/04 11/17/04 11/18/04 11/19/04 11/20/04 11/21/04', 361 '11/22/04 11/23/04 11/24/04 11/25/04 11/26/04 11/27/04 11/28/04', 362 '11/29/04 11/30/04 12/01/04 12/02/04 12/03/04 12/04/04 12/05/04'], 363 ['11/29/04 11/30/04 12/01/04 12/02/04 12/03/04 12/04/04 12/05/04', 364 '12/06/04 12/07/04 12/08/04 12/09/04 12/10/04 12/11/04 12/12/04', 365 '12/13/04 12/14/04 12/15/04 12/16/04 12/17/04 12/18/04 12/19/04', 366 '12/20/04 12/21/04 12/22/04 12/23/04 12/24/04 12/25/04 12/26/04', 367 '12/27/04 12/28/04 12/29/04 12/30/04 12/31/04 01/01/05 01/02/05']]] 368 369 370class OutputTestCase(unittest.TestCase): 371 def normalize_calendar(self, s): 372 # Filters out locale dependent strings 373 def neitherspacenordigit(c): 374 return not c.isspace() and not c.isdigit() 375 376 lines = [] 377 for line in s.splitlines(keepends=False): 378 # Drop texts, as they are locale dependent 379 if line and not filter(neitherspacenordigit, line): 380 lines.append(line) 381 return lines 382 383 def check_htmlcalendar_encoding(self, req, res): 384 cal = calendar.HTMLCalendar() 385 format_ = default_format.copy() 386 format_["encoding"] = req or 'utf-8' 387 output = cal.formatyearpage(2004, encoding=req) 388 self.assertEqual( 389 output, 390 result_2004_html.format(**format_).encode(res) 391 ) 392 393 def test_output(self): 394 self.assertEqual( 395 self.normalize_calendar(calendar.calendar(2004)), 396 self.normalize_calendar(result_2004_text) 397 ) 398 self.assertEqual( 399 self.normalize_calendar(calendar.calendar(0)), 400 self.normalize_calendar(result_0_text) 401 ) 402 403 def test_output_textcalendar(self): 404 self.assertEqual( 405 calendar.TextCalendar().formatyear(2004), 406 result_2004_text 407 ) 408 self.assertEqual( 409 calendar.TextCalendar().formatyear(0), 410 result_0_text 411 ) 412 413 def test_output_htmlcalendar_encoding_ascii(self): 414 self.check_htmlcalendar_encoding('ascii', 'ascii') 415 416 def test_output_htmlcalendar_encoding_utf8(self): 417 self.check_htmlcalendar_encoding('utf-8', 'utf-8') 418 419 def test_output_htmlcalendar_encoding_default(self): 420 self.check_htmlcalendar_encoding(None, sys.getdefaultencoding()) 421 422 def test_yeardatescalendar(self): 423 def shrink(cal): 424 return [[[' '.join('{:02d}/{:02d}/{}'.format( 425 d.month, d.day, str(d.year)[-2:]) for d in z) 426 for z in y] for y in x] for x in cal] 427 self.assertEqual( 428 shrink(calendar.Calendar().yeardatescalendar(2004)), 429 result_2004_dates 430 ) 431 432 def test_yeardayscalendar(self): 433 self.assertEqual( 434 calendar.Calendar().yeardayscalendar(2004), 435 result_2004_days 436 ) 437 438 def test_formatweekheader_short(self): 439 self.assertEqual( 440 calendar.TextCalendar().formatweekheader(2), 441 'Mo Tu We Th Fr Sa Su' 442 ) 443 444 def test_formatweekheader_long(self): 445 self.assertEqual( 446 calendar.TextCalendar().formatweekheader(9), 447 ' Monday Tuesday Wednesday Thursday ' 448 ' Friday Saturday Sunday ' 449 ) 450 451 def test_formatmonth(self): 452 self.assertEqual( 453 calendar.TextCalendar().formatmonth(2004, 1), 454 result_2004_01_text 455 ) 456 self.assertEqual( 457 calendar.TextCalendar().formatmonth(0, 2), 458 result_0_02_text 459 ) 460 def test_formatmonth_with_invalid_month(self): 461 with self.assertRaises(calendar.IllegalMonthError): 462 calendar.TextCalendar().formatmonth(2017, 13) 463 with self.assertRaises(calendar.IllegalMonthError): 464 calendar.TextCalendar().formatmonth(2017, -1) 465 466 def test_formatmonthname_with_year(self): 467 self.assertEqual( 468 calendar.HTMLCalendar().formatmonthname(2004, 1, withyear=True), 469 '<tr><th colspan="7" class="month">January 2004</th></tr>' 470 ) 471 472 def test_formatmonthname_without_year(self): 473 self.assertEqual( 474 calendar.HTMLCalendar().formatmonthname(2004, 1, withyear=False), 475 '<tr><th colspan="7" class="month">January</th></tr>' 476 ) 477 478 def test_prweek(self): 479 with support.captured_stdout() as out: 480 week = [(1,0), (2,1), (3,2), (4,3), (5,4), (6,5), (7,6)] 481 calendar.TextCalendar().prweek(week, 1) 482 self.assertEqual(out.getvalue(), " 1 2 3 4 5 6 7") 483 484 def test_prmonth(self): 485 with support.captured_stdout() as out: 486 calendar.TextCalendar().prmonth(2004, 1) 487 self.assertEqual(out.getvalue(), result_2004_01_text) 488 489 def test_pryear(self): 490 with support.captured_stdout() as out: 491 calendar.TextCalendar().pryear(2004) 492 self.assertEqual(out.getvalue(), result_2004_text) 493 494 def test_format(self): 495 with support.captured_stdout() as out: 496 calendar.format(["1", "2", "3"], colwidth=3, spacing=1) 497 self.assertEqual(out.getvalue().strip(), "1 2 3") 498 499class CalendarTestCase(unittest.TestCase): 500 501 def test_deprecation_warning(self): 502 with self.assertWarnsRegex( 503 DeprecationWarning, 504 "The 'January' attribute is deprecated, use 'JANUARY' instead" 505 ): 506 calendar.January 507 508 def test_isleap(self): 509 # Make sure that the return is right for a few years, and 510 # ensure that the return values are 1 or 0, not just true or 511 # false (see SF bug #485794). Specific additional tests may 512 # be appropriate; this tests a single "cycle". 513 self.assertEqual(calendar.isleap(2000), 1) 514 self.assertEqual(calendar.isleap(2001), 0) 515 self.assertEqual(calendar.isleap(2002), 0) 516 self.assertEqual(calendar.isleap(2003), 0) 517 518 def test_setfirstweekday(self): 519 self.assertRaises(TypeError, calendar.setfirstweekday, 'flabber') 520 self.assertRaises(ValueError, calendar.setfirstweekday, -1) 521 self.assertRaises(ValueError, calendar.setfirstweekday, 200) 522 orig = calendar.firstweekday() 523 calendar.setfirstweekday(calendar.SUNDAY) 524 self.assertEqual(calendar.firstweekday(), calendar.SUNDAY) 525 calendar.setfirstweekday(calendar.MONDAY) 526 self.assertEqual(calendar.firstweekday(), calendar.MONDAY) 527 calendar.setfirstweekday(orig) 528 529 def test_illegal_weekday_reported(self): 530 with self.assertRaisesRegex(calendar.IllegalWeekdayError, '123'): 531 calendar.setfirstweekday(123) 532 533 def test_enumerate_weekdays(self): 534 self.assertRaises(IndexError, calendar.day_abbr.__getitem__, -10) 535 self.assertRaises(IndexError, calendar.day_name.__getitem__, 10) 536 self.assertEqual(len([d for d in calendar.day_abbr]), 7) 537 538 def test_days(self): 539 for attr in "day_name", "day_abbr": 540 value = getattr(calendar, attr) 541 self.assertEqual(len(value), 7) 542 self.assertEqual(len(value[:]), 7) 543 # ensure they're all unique 544 self.assertEqual(len(set(value)), 7) 545 # verify it "acts like a sequence" in two forms of iteration 546 self.assertEqual(value[::-1], list(reversed(value))) 547 548 def test_months(self): 549 for attr in "month_name", "month_abbr": 550 value = getattr(calendar, attr) 551 self.assertEqual(len(value), 13) 552 self.assertEqual(len(value[:]), 13) 553 self.assertEqual(value[0], "") 554 # ensure they're all unique 555 self.assertEqual(len(set(value)), 13) 556 # verify it "acts like a sequence" in two forms of iteration 557 self.assertEqual(value[::-1], list(reversed(value))) 558 559 def test_locale_text_calendar(self): 560 try: 561 cal = calendar.LocaleTextCalendar(locale='') 562 local_weekday = cal.formatweekday(1, 10) 563 local_weekday_abbr = cal.formatweekday(1, 3) 564 local_month = cal.formatmonthname(2010, 10, 10) 565 except locale.Error: 566 # cannot set the system default locale -- skip rest of test 567 raise unittest.SkipTest('cannot set the system default locale') 568 self.assertIsInstance(local_weekday, str) 569 self.assertIsInstance(local_weekday_abbr, str) 570 self.assertIsInstance(local_month, str) 571 self.assertEqual(len(local_weekday), 10) 572 self.assertEqual(len(local_weekday_abbr), 3) 573 self.assertGreaterEqual(len(local_month), 10) 574 575 cal = calendar.LocaleTextCalendar(locale=None) 576 local_weekday = cal.formatweekday(1, 10) 577 local_weekday_abbr = cal.formatweekday(1, 3) 578 local_month = cal.formatmonthname(2010, 10, 10) 579 self.assertIsInstance(local_weekday, str) 580 self.assertIsInstance(local_weekday_abbr, str) 581 self.assertIsInstance(local_month, str) 582 self.assertEqual(len(local_weekday), 10) 583 self.assertEqual(len(local_weekday_abbr), 3) 584 self.assertGreaterEqual(len(local_month), 10) 585 586 cal = calendar.LocaleTextCalendar(locale='C') 587 local_weekday = cal.formatweekday(1, 10) 588 local_weekday_abbr = cal.formatweekday(1, 3) 589 local_month = cal.formatmonthname(2010, 10, 10) 590 self.assertIsInstance(local_weekday, str) 591 self.assertIsInstance(local_weekday_abbr, str) 592 self.assertIsInstance(local_month, str) 593 self.assertEqual(len(local_weekday), 10) 594 self.assertEqual(len(local_weekday_abbr), 3) 595 self.assertGreaterEqual(len(local_month), 10) 596 597 def test_locale_html_calendar(self): 598 try: 599 cal = calendar.LocaleHTMLCalendar(locale='') 600 local_weekday = cal.formatweekday(1) 601 local_month = cal.formatmonthname(2010, 10) 602 except locale.Error: 603 # cannot set the system default locale -- skip rest of test 604 raise unittest.SkipTest('cannot set the system default locale') 605 self.assertIsInstance(local_weekday, str) 606 self.assertIsInstance(local_month, str) 607 608 cal = calendar.LocaleHTMLCalendar(locale=None) 609 local_weekday = cal.formatweekday(1) 610 local_month = cal.formatmonthname(2010, 10) 611 self.assertIsInstance(local_weekday, str) 612 self.assertIsInstance(local_month, str) 613 614 cal = calendar.LocaleHTMLCalendar(locale='C') 615 local_weekday = cal.formatweekday(1) 616 local_month = cal.formatmonthname(2010, 10) 617 self.assertIsInstance(local_weekday, str) 618 self.assertIsInstance(local_month, str) 619 620 def test_locale_calendars_reset_locale_properly(self): 621 # ensure that Locale{Text,HTML}Calendar resets the locale properly 622 # (it is still not thread-safe though) 623 old_october = calendar.TextCalendar().formatmonthname(2010, 10, 10) 624 try: 625 cal = calendar.LocaleTextCalendar(locale='') 626 local_weekday = cal.formatweekday(1, 10) 627 local_weekday_abbr = cal.formatweekday(1, 3) 628 local_month = cal.formatmonthname(2010, 10, 10) 629 except locale.Error: 630 # cannot set the system default locale -- skip rest of test 631 raise unittest.SkipTest('cannot set the system default locale') 632 self.assertIsInstance(local_weekday, str) 633 self.assertIsInstance(local_weekday_abbr, str) 634 self.assertIsInstance(local_month, str) 635 self.assertEqual(len(local_weekday), 10) 636 self.assertEqual(len(local_weekday_abbr), 3) 637 self.assertGreaterEqual(len(local_month), 10) 638 639 cal = calendar.LocaleHTMLCalendar(locale='') 640 local_weekday = cal.formatweekday(1) 641 local_month = cal.formatmonthname(2010, 10) 642 self.assertIsInstance(local_weekday, str) 643 self.assertIsInstance(local_month, str) 644 645 new_october = calendar.TextCalendar().formatmonthname(2010, 10, 10) 646 self.assertEqual(old_october, new_october) 647 648 def test_locale_calendar_formatweekday(self): 649 try: 650 # formatweekday uses different day names based on the available width. 651 cal = calendar.LocaleTextCalendar(locale='en_US') 652 # For really short widths, the abbreviated name is truncated. 653 self.assertEqual(cal.formatweekday(0, 1), "M") 654 self.assertEqual(cal.formatweekday(0, 2), "Mo") 655 # For short widths, a centered, abbreviated name is used. 656 self.assertEqual(cal.formatweekday(0, 3), "Mon") 657 self.assertEqual(cal.formatweekday(0, 5), " Mon ") 658 self.assertEqual(cal.formatweekday(0, 8), " Mon ") 659 # For long widths, the full day name is used. 660 self.assertEqual(cal.formatweekday(0, 9), " Monday ") 661 self.assertEqual(cal.formatweekday(0, 10), " Monday ") 662 except locale.Error: 663 raise unittest.SkipTest('cannot set the en_US locale') 664 665 def test_locale_calendar_formatmonthname(self): 666 try: 667 # formatmonthname uses the same month names regardless of the width argument. 668 cal = calendar.LocaleTextCalendar(locale='en_US') 669 # For too short widths, a full name (with year) is used. 670 self.assertEqual(cal.formatmonthname(2022, 6, 2, withyear=False), "June") 671 self.assertEqual(cal.formatmonthname(2022, 6, 2, withyear=True), "June 2022") 672 self.assertEqual(cal.formatmonthname(2022, 6, 3, withyear=False), "June") 673 self.assertEqual(cal.formatmonthname(2022, 6, 3, withyear=True), "June 2022") 674 # For long widths, a centered name is used. 675 self.assertEqual(cal.formatmonthname(2022, 6, 10, withyear=False), " June ") 676 self.assertEqual(cal.formatmonthname(2022, 6, 15, withyear=True), " June 2022 ") 677 except locale.Error: 678 raise unittest.SkipTest('cannot set the en_US locale') 679 680 def test_locale_html_calendar_custom_css_class_month_name(self): 681 try: 682 cal = calendar.LocaleHTMLCalendar(locale='') 683 local_month = cal.formatmonthname(2010, 10, 10) 684 except locale.Error: 685 # cannot set the system default locale -- skip rest of test 686 raise unittest.SkipTest('cannot set the system default locale') 687 self.assertIn('class="month"', local_month) 688 cal.cssclass_month_head = "text-center month" 689 local_month = cal.formatmonthname(2010, 10, 10) 690 self.assertIn('class="text-center month"', local_month) 691 692 def test_locale_html_calendar_custom_css_class_weekday(self): 693 try: 694 cal = calendar.LocaleHTMLCalendar(locale='') 695 local_weekday = cal.formatweekday(6) 696 except locale.Error: 697 # cannot set the system default locale -- skip rest of test 698 raise unittest.SkipTest('cannot set the system default locale') 699 self.assertIn('class="sun"', local_weekday) 700 cal.cssclasses_weekday_head = ["mon2", "tue2", "wed2", "thu2", "fri2", "sat2", "sun2"] 701 local_weekday = cal.formatweekday(6) 702 self.assertIn('class="sun2"', local_weekday) 703 704 def test_itermonthdays3(self): 705 # ensure itermonthdays3 doesn't overflow after datetime.MAXYEAR 706 list(calendar.Calendar().itermonthdays3(datetime.MAXYEAR, 12)) 707 708 def test_itermonthdays4(self): 709 cal = calendar.Calendar(firstweekday=3) 710 days = list(cal.itermonthdays4(2001, 2)) 711 self.assertEqual(days[0], (2001, 2, 1, 3)) 712 self.assertEqual(days[-1], (2001, 2, 28, 2)) 713 714 def test_itermonthdays(self): 715 for firstweekday in range(7): 716 cal = calendar.Calendar(firstweekday) 717 # Test the extremes, see #28253 and #26650 718 for y, m in [(1, 1), (9999, 12)]: 719 days = list(cal.itermonthdays(y, m)) 720 self.assertIn(len(days), (35, 42)) 721 # Test a short month 722 cal = calendar.Calendar(firstweekday=3) 723 days = list(cal.itermonthdays(2001, 2)) 724 self.assertEqual(days, list(range(1, 29))) 725 726 def test_itermonthdays2(self): 727 for firstweekday in range(7): 728 cal = calendar.Calendar(firstweekday) 729 # Test the extremes, see #28253 and #26650 730 for y, m in [(1, 1), (9999, 12)]: 731 days = list(cal.itermonthdays2(y, m)) 732 self.assertEqual(days[0][1], firstweekday) 733 self.assertEqual(days[-1][1], (firstweekday - 1) % 7) 734 735 def test_iterweekdays(self): 736 week0 = list(range(7)) 737 for firstweekday in range(7): 738 cal = calendar.Calendar(firstweekday) 739 week = list(cal.iterweekdays()) 740 expected = week0[firstweekday:] + week0[:firstweekday] 741 self.assertEqual(week, expected) 742 743 744class MonthCalendarTestCase(unittest.TestCase): 745 def setUp(self): 746 self.oldfirstweekday = calendar.firstweekday() 747 calendar.setfirstweekday(self.firstweekday) 748 749 def tearDown(self): 750 calendar.setfirstweekday(self.oldfirstweekday) 751 752 def check_weeks(self, year, month, weeks): 753 cal = calendar.monthcalendar(year, month) 754 self.assertEqual(len(cal), len(weeks)) 755 for i in range(len(weeks)): 756 self.assertEqual(weeks[i], sum(day != 0 for day in cal[i])) 757 758 759class MondayTestCase(MonthCalendarTestCase): 760 firstweekday = calendar.MONDAY 761 762 def test_february(self): 763 # A 28-day february starting on monday (7+7+7+7 days) 764 self.check_weeks(1999, 2, (7, 7, 7, 7)) 765 766 # A 28-day february starting on tuesday (6+7+7+7+1 days) 767 self.check_weeks(2005, 2, (6, 7, 7, 7, 1)) 768 769 # A 28-day february starting on sunday (1+7+7+7+6 days) 770 self.check_weeks(1987, 2, (1, 7, 7, 7, 6)) 771 772 # A 29-day february starting on monday (7+7+7+7+1 days) 773 self.check_weeks(1988, 2, (7, 7, 7, 7, 1)) 774 775 # A 29-day february starting on tuesday (6+7+7+7+2 days) 776 self.check_weeks(1972, 2, (6, 7, 7, 7, 2)) 777 778 # A 29-day february starting on sunday (1+7+7+7+7 days) 779 self.check_weeks(2004, 2, (1, 7, 7, 7, 7)) 780 781 def test_april(self): 782 # A 30-day april starting on monday (7+7+7+7+2 days) 783 self.check_weeks(1935, 4, (7, 7, 7, 7, 2)) 784 785 # A 30-day april starting on tuesday (6+7+7+7+3 days) 786 self.check_weeks(1975, 4, (6, 7, 7, 7, 3)) 787 788 # A 30-day april starting on sunday (1+7+7+7+7+1 days) 789 self.check_weeks(1945, 4, (1, 7, 7, 7, 7, 1)) 790 791 # A 30-day april starting on saturday (2+7+7+7+7 days) 792 self.check_weeks(1995, 4, (2, 7, 7, 7, 7)) 793 794 # A 30-day april starting on friday (3+7+7+7+6 days) 795 self.check_weeks(1994, 4, (3, 7, 7, 7, 6)) 796 797 def test_december(self): 798 # A 31-day december starting on monday (7+7+7+7+3 days) 799 self.check_weeks(1980, 12, (7, 7, 7, 7, 3)) 800 801 # A 31-day december starting on tuesday (6+7+7+7+4 days) 802 self.check_weeks(1987, 12, (6, 7, 7, 7, 4)) 803 804 # A 31-day december starting on sunday (1+7+7+7+7+2 days) 805 self.check_weeks(1968, 12, (1, 7, 7, 7, 7, 2)) 806 807 # A 31-day december starting on thursday (4+7+7+7+6 days) 808 self.check_weeks(1988, 12, (4, 7, 7, 7, 6)) 809 810 # A 31-day december starting on friday (3+7+7+7+7 days) 811 self.check_weeks(2017, 12, (3, 7, 7, 7, 7)) 812 813 # A 31-day december starting on saturday (2+7+7+7+7+1 days) 814 self.check_weeks(2068, 12, (2, 7, 7, 7, 7, 1)) 815 816 817class SundayTestCase(MonthCalendarTestCase): 818 firstweekday = calendar.SUNDAY 819 820 def test_february(self): 821 # A 28-day february starting on sunday (7+7+7+7 days) 822 self.check_weeks(2009, 2, (7, 7, 7, 7)) 823 824 # A 28-day february starting on monday (6+7+7+7+1 days) 825 self.check_weeks(1999, 2, (6, 7, 7, 7, 1)) 826 827 # A 28-day february starting on saturday (1+7+7+7+6 days) 828 self.check_weeks(1997, 2, (1, 7, 7, 7, 6)) 829 830 # A 29-day february starting on sunday (7+7+7+7+1 days) 831 self.check_weeks(2004, 2, (7, 7, 7, 7, 1)) 832 833 # A 29-day february starting on monday (6+7+7+7+2 days) 834 self.check_weeks(1960, 2, (6, 7, 7, 7, 2)) 835 836 # A 29-day february starting on saturday (1+7+7+7+7 days) 837 self.check_weeks(1964, 2, (1, 7, 7, 7, 7)) 838 839 def test_april(self): 840 # A 30-day april starting on sunday (7+7+7+7+2 days) 841 self.check_weeks(1923, 4, (7, 7, 7, 7, 2)) 842 843 # A 30-day april starting on monday (6+7+7+7+3 days) 844 self.check_weeks(1918, 4, (6, 7, 7, 7, 3)) 845 846 # A 30-day april starting on saturday (1+7+7+7+7+1 days) 847 self.check_weeks(1950, 4, (1, 7, 7, 7, 7, 1)) 848 849 # A 30-day april starting on friday (2+7+7+7+7 days) 850 self.check_weeks(1960, 4, (2, 7, 7, 7, 7)) 851 852 # A 30-day april starting on thursday (3+7+7+7+6 days) 853 self.check_weeks(1909, 4, (3, 7, 7, 7, 6)) 854 855 def test_december(self): 856 # A 31-day december starting on sunday (7+7+7+7+3 days) 857 self.check_weeks(2080, 12, (7, 7, 7, 7, 3)) 858 859 # A 31-day december starting on monday (6+7+7+7+4 days) 860 self.check_weeks(1941, 12, (6, 7, 7, 7, 4)) 861 862 # A 31-day december starting on saturday (1+7+7+7+7+2 days) 863 self.check_weeks(1923, 12, (1, 7, 7, 7, 7, 2)) 864 865 # A 31-day december starting on wednesday (4+7+7+7+6 days) 866 self.check_weeks(1948, 12, (4, 7, 7, 7, 6)) 867 868 # A 31-day december starting on thursday (3+7+7+7+7 days) 869 self.check_weeks(1927, 12, (3, 7, 7, 7, 7)) 870 871 # A 31-day december starting on friday (2+7+7+7+7+1 days) 872 self.check_weeks(1995, 12, (2, 7, 7, 7, 7, 1)) 873 874class TimegmTestCase(unittest.TestCase): 875 TIMESTAMPS = [0, 10, 100, 1000, 10000, 100000, 1000000, 876 1234567890, 1262304000, 1275785153,] 877 def test_timegm(self): 878 for secs in self.TIMESTAMPS: 879 tuple = time.gmtime(secs) 880 self.assertEqual(secs, calendar.timegm(tuple)) 881 882class MonthRangeTestCase(unittest.TestCase): 883 def test_january(self): 884 # Tests valid lower boundary case. 885 self.assertEqual(calendar.monthrange(2004,1), (3,31)) 886 887 def test_february_leap(self): 888 # Tests February during leap year. 889 self.assertEqual(calendar.monthrange(2004,2), (6,29)) 890 891 def test_february_nonleap(self): 892 # Tests February in non-leap year. 893 self.assertEqual(calendar.monthrange(2010,2), (0,28)) 894 895 def test_december(self): 896 # Tests valid upper boundary case. 897 self.assertEqual(calendar.monthrange(2004,12), (2,31)) 898 899 def test_zeroth_month(self): 900 # Tests low invalid boundary case. 901 with self.assertRaises(calendar.IllegalMonthError): 902 calendar.monthrange(2004, 0) 903 904 def test_thirteenth_month(self): 905 # Tests high invalid boundary case. 906 with self.assertRaises(calendar.IllegalMonthError): 907 calendar.monthrange(2004, 13) 908 909 def test_illegal_month_reported(self): 910 with self.assertRaisesRegex(calendar.IllegalMonthError, '65'): 911 calendar.monthrange(2004, 65) 912 913class LeapdaysTestCase(unittest.TestCase): 914 def test_no_range(self): 915 # test when no range i.e. two identical years as args 916 self.assertEqual(calendar.leapdays(2010,2010), 0) 917 918 def test_no_leapdays(self): 919 # test when no leap years in range 920 self.assertEqual(calendar.leapdays(2010,2011), 0) 921 922 def test_no_leapdays_upper_boundary(self): 923 # test no leap years in range, when upper boundary is a leap year 924 self.assertEqual(calendar.leapdays(2010,2012), 0) 925 926 def test_one_leapday_lower_boundary(self): 927 # test when one leap year in range, lower boundary is leap year 928 self.assertEqual(calendar.leapdays(2012,2013), 1) 929 930 def test_several_leapyears_in_range(self): 931 self.assertEqual(calendar.leapdays(1997,2020), 5) 932 933 934def conv(s): 935 return s.replace('\n', os.linesep).encode() 936 937class CommandLineTestCase(unittest.TestCase): 938 def setUp(self): 939 self.runners = [self.run_cli_ok, self.run_cmd_ok] 940 941 @contextlib.contextmanager 942 def captured_stdout_with_buffer(self): 943 orig_stdout = sys.stdout 944 buffer = io.BytesIO() 945 sys.stdout = io.TextIOWrapper(buffer) 946 try: 947 yield sys.stdout 948 finally: 949 sys.stdout.flush() 950 sys.stdout.buffer.seek(0) 951 sys.stdout = orig_stdout 952 953 @contextlib.contextmanager 954 def captured_stderr_with_buffer(self): 955 orig_stderr = sys.stderr 956 buffer = io.BytesIO() 957 sys.stderr = io.TextIOWrapper(buffer) 958 try: 959 yield sys.stderr 960 finally: 961 sys.stderr.flush() 962 sys.stderr.buffer.seek(0) 963 sys.stderr = orig_stderr 964 965 def run_cli_ok(self, *args): 966 with self.captured_stdout_with_buffer() as stdout: 967 calendar.main(args) 968 return stdout.buffer.read() 969 970 def run_cmd_ok(self, *args): 971 return assert_python_ok('-m', 'calendar', *args)[1] 972 973 def assertCLIFails(self, *args): 974 with self.captured_stderr_with_buffer() as stderr: 975 self.assertRaises(SystemExit, calendar.main, args) 976 stderr = stderr.buffer.read() 977 self.assertIn(b'usage:', stderr) 978 return stderr 979 980 def assertCmdFails(self, *args): 981 rc, stdout, stderr = assert_python_failure('-m', 'calendar', *args) 982 self.assertIn(b'usage:', stderr) 983 self.assertEqual(rc, 2) 984 return rc, stdout, stderr 985 986 def assertFailure(self, *args): 987 self.assertCLIFails(*args) 988 self.assertCmdFails(*args) 989 990 def test_help(self): 991 stdout = self.run_cmd_ok('-h') 992 self.assertIn(b'usage:', stdout) 993 self.assertIn(b'calendar.py', stdout) 994 self.assertIn(b'--help', stdout) 995 996 # special case: stdout but sys.exit() 997 with self.captured_stdout_with_buffer() as output: 998 self.assertRaises(SystemExit, calendar.main, ['-h']) 999 output = output.buffer.read() 1000 self.assertIn(b'usage:', output) 1001 self.assertIn(b'--help', output) 1002 1003 def test_illegal_arguments(self): 1004 self.assertFailure('-z') 1005 self.assertFailure('spam') 1006 self.assertFailure('2004', 'spam') 1007 self.assertFailure('2004', '1', 'spam') 1008 self.assertFailure('2004', '1', '1') 1009 self.assertFailure('2004', '1', '1', 'spam') 1010 self.assertFailure('-t', 'html', '2004', '1') 1011 1012 def test_output_current_year(self): 1013 for run in self.runners: 1014 output = run() 1015 year = datetime.datetime.now().year 1016 self.assertIn(conv(' %s' % year), output) 1017 self.assertIn(b'January', output) 1018 self.assertIn(b'Mo Tu We Th Fr Sa Su', output) 1019 1020 def test_output_year(self): 1021 for run in self.runners: 1022 output = run('2004') 1023 self.assertEqual(output, conv(result_2004_text)) 1024 1025 def test_output_month(self): 1026 for run in self.runners: 1027 output = run('2004', '1') 1028 self.assertEqual(output, conv(result_2004_01_text)) 1029 1030 def test_option_encoding(self): 1031 self.assertFailure('-e') 1032 self.assertFailure('--encoding') 1033 for run in self.runners: 1034 output = run('--encoding', 'utf-16-le', '2004') 1035 self.assertEqual(output, result_2004_text.encode('utf-16-le')) 1036 1037 def test_option_locale(self): 1038 self.assertFailure('-L') 1039 self.assertFailure('--locale') 1040 self.assertFailure('-L', 'en') 1041 1042 lang, enc = locale.getlocale() 1043 lang = lang or 'C' 1044 enc = enc or 'UTF-8' 1045 try: 1046 oldlocale = locale.getlocale(locale.LC_TIME) 1047 try: 1048 locale.setlocale(locale.LC_TIME, (lang, enc)) 1049 finally: 1050 locale.setlocale(locale.LC_TIME, oldlocale) 1051 except (locale.Error, ValueError): 1052 self.skipTest('cannot set the system default locale') 1053 for run in self.runners: 1054 for type in ('text', 'html'): 1055 output = run( 1056 '--type', type, '--locale', lang, '--encoding', enc, '2004' 1057 ) 1058 self.assertIn('2004'.encode(enc), output) 1059 1060 def test_option_width(self): 1061 self.assertFailure('-w') 1062 self.assertFailure('--width') 1063 self.assertFailure('-w', 'spam') 1064 for run in self.runners: 1065 output = run('--width', '3', '2004') 1066 self.assertIn(b'Mon Tue Wed Thu Fri Sat Sun', output) 1067 1068 def test_option_lines(self): 1069 self.assertFailure('-l') 1070 self.assertFailure('--lines') 1071 self.assertFailure('-l', 'spam') 1072 for run in self.runners: 1073 output = run('--lines', '2', '2004') 1074 self.assertIn(conv('December\n\nMo Tu We'), output) 1075 1076 def test_option_spacing(self): 1077 self.assertFailure('-s') 1078 self.assertFailure('--spacing') 1079 self.assertFailure('-s', 'spam') 1080 for run in self.runners: 1081 output = run('--spacing', '8', '2004') 1082 self.assertIn(b'Su Mo', output) 1083 1084 def test_option_months(self): 1085 self.assertFailure('-m') 1086 self.assertFailure('--month') 1087 self.assertFailure('-m', 'spam') 1088 for run in self.runners: 1089 output = run('--months', '1', '2004') 1090 self.assertIn(conv('\nMo Tu We Th Fr Sa Su\n'), output) 1091 1092 def test_option_type(self): 1093 self.assertFailure('-t') 1094 self.assertFailure('--type') 1095 self.assertFailure('-t', 'spam') 1096 for run in self.runners: 1097 output = run('--type', 'text', '2004') 1098 self.assertEqual(output, conv(result_2004_text)) 1099 output = run('--type', 'html', '2004') 1100 self.assertEqual(output[:6], b'<?xml ') 1101 self.assertIn(b'<title>Calendar for 2004</title>', output) 1102 1103 def test_html_output_current_year(self): 1104 for run in self.runners: 1105 output = run('--type', 'html') 1106 year = datetime.datetime.now().year 1107 self.assertIn(('<title>Calendar for %s</title>' % year).encode(), output) 1108 self.assertIn(b'<tr><th colspan="7" class="month">January</th></tr>', output) 1109 1110 def test_html_output_year_encoding(self): 1111 for run in self.runners: 1112 output = run('-t', 'html', '--encoding', 'ascii', '2004') 1113 self.assertEqual(output, result_2004_html.format(**default_format).encode('ascii')) 1114 1115 def test_html_output_year_css(self): 1116 self.assertFailure('-t', 'html', '-c') 1117 self.assertFailure('-t', 'html', '--css') 1118 for run in self.runners: 1119 output = run('-t', 'html', '--css', 'custom.css', '2004') 1120 self.assertIn(b'<link rel="stylesheet" type="text/css" ' 1121 b'href="custom.css" />', output) 1122 1123 1124class MiscTestCase(unittest.TestCase): 1125 def test__all__(self): 1126 not_exported = { 1127 'mdays', 'January', 'February', 'EPOCH', 1128 'different_locale', 'c', 'prweek', 'week', 'format', 1129 'formatstring', 'main', 'monthlen', 'prevmonth', 'nextmonth', ""} 1130 support.check__all__(self, calendar, not_exported=not_exported) 1131 1132 1133class TestSubClassingCase(unittest.TestCase): 1134 1135 def setUp(self): 1136 1137 class CustomHTMLCal(calendar.HTMLCalendar): 1138 cssclasses = [style + " text-nowrap" for style in 1139 calendar.HTMLCalendar.cssclasses] 1140 cssclasses_weekday_head = ["red", "blue", "green", "lilac", 1141 "yellow", "orange", "pink"] 1142 cssclass_month_head = "text-center month-head" 1143 cssclass_month = "text-center month" 1144 cssclass_year = "text-italic " 1145 cssclass_year_head = "lead " 1146 1147 self.cal = CustomHTMLCal() 1148 1149 def test_formatmonthname(self): 1150 self.assertIn('class="text-center month-head"', 1151 self.cal.formatmonthname(2017, 5)) 1152 1153 def test_formatmonth(self): 1154 self.assertIn('class="text-center month"', 1155 self.cal.formatmonth(2017, 5)) 1156 1157 def test_formatmonth_with_invalid_month(self): 1158 with self.assertRaises(calendar.IllegalMonthError): 1159 self.cal.formatmonth(2017, 13) 1160 with self.assertRaises(calendar.IllegalMonthError): 1161 self.cal.formatmonth(2017, -1) 1162 1163 1164 def test_formatweek(self): 1165 weeks = self.cal.monthdays2calendar(2017, 5) 1166 self.assertIn('class="wed text-nowrap"', self.cal.formatweek(weeks[0])) 1167 1168 def test_formatweek_head(self): 1169 header = self.cal.formatweekheader() 1170 for color in self.cal.cssclasses_weekday_head: 1171 self.assertIn('<th class="%s">' % color, header) 1172 1173 def test_format_year(self): 1174 self.assertIn( 1175 ('<table border="0" cellpadding="0" cellspacing="0" class="%s">' % 1176 self.cal.cssclass_year), self.cal.formatyear(2017)) 1177 1178 def test_format_year_head(self): 1179 self.assertIn('<tr><th colspan="%d" class="%s">%s</th></tr>' % ( 1180 3, self.cal.cssclass_year_head, 2017), self.cal.formatyear(2017)) 1181 1182if __name__ == "__main__": 1183 unittest.main() 1184