Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
a98924a0
Commit
a98924a0
authored
May 17, 2003
by
Tim Peters
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
datetime.datetime and datetime.time can now be subclassed in Python. Brr.
parent
63af08aa
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
180 additions
and
61 deletions
+180
-61
Lib/test/test_datetime.py
Lib/test/test_datetime.py
+131
-31
Misc/NEWS
Misc/NEWS
+3
-0
Modules/datetimemodule.c
Modules/datetimemodule.c
+46
-30
No files found.
Lib/test/test_datetime.py
View file @
a98924a0
...
...
@@ -479,37 +479,6 @@ class TestDateOnly(unittest.TestCase):
dt2
=
dt
-
delta
self
.
assertEqual
(
dt2
,
dt
-
days
)
def
test_subclass_date
(
self
):
# XXX When datetime becomes usably subclassable, uncomment the
# XXX "self.theclass" lines and move this into TestDate.
# class C(self.theclass):
class
C
(
date
):
theAnswer
=
42
def
__new__
(
cls
,
*
args
,
**
kws
):
temp
=
kws
.
copy
()
extra
=
temp
.
pop
(
'extra'
)
# result = self.theclass.__new__(cls, *args, **temp)
result
=
date
.
__new__
(
cls
,
*
args
,
**
temp
)
result
.
extra
=
extra
return
result
def
newmeth
(
self
,
start
):
return
start
+
self
.
year
+
self
.
month
args
=
2003
,
4
,
14
# dt1 = self.theclass(*args)
dt1
=
date
(
*
args
)
dt2
=
C
(
*
args
,
**
{
'extra'
:
7
})
self
.
assertEqual
(
dt2
.
__class__
,
C
)
self
.
assertEqual
(
dt2
.
theAnswer
,
42
)
self
.
assertEqual
(
dt2
.
extra
,
7
)
self
.
assertEqual
(
dt1
.
toordinal
(),
dt2
.
toordinal
())
self
.
assertEqual
(
dt2
.
newmeth
(
-
7
),
dt1
.
year
+
dt1
.
month
-
7
)
class
TestDate
(
HarmlessMixedComparison
):
# Tests here should pass for both dates and datetimes, except for a
# few tests that TestDateTime overrides.
...
...
@@ -1002,6 +971,32 @@ class TestDate(HarmlessMixedComparison):
base
=
cls
(
2000
,
2
,
29
)
self
.
assertRaises
(
ValueError
,
base
.
replace
,
year
=
2001
)
def
test_subclass_date
(
self
):
class
C
(
self
.
theclass
):
theAnswer
=
42
def
__new__
(
cls
,
*
args
,
**
kws
):
temp
=
kws
.
copy
()
extra
=
temp
.
pop
(
'extra'
)
result
=
self
.
theclass
.
__new__
(
cls
,
*
args
,
**
temp
)
result
.
extra
=
extra
return
result
def
newmeth
(
self
,
start
):
return
start
+
self
.
year
+
self
.
month
args
=
2003
,
4
,
14
dt1
=
self
.
theclass
(
*
args
)
dt2
=
C
(
*
args
,
**
{
'extra'
:
7
})
self
.
assertEqual
(
dt2
.
__class__
,
C
)
self
.
assertEqual
(
dt2
.
theAnswer
,
42
)
self
.
assertEqual
(
dt2
.
extra
,
7
)
self
.
assertEqual
(
dt1
.
toordinal
(),
dt2
.
toordinal
())
self
.
assertEqual
(
dt2
.
newmeth
(
-
7
),
dt1
.
year
+
dt1
.
month
-
7
)
#############################################################################
# datetime tests
...
...
@@ -1426,6 +1421,33 @@ class TestDateTime(TestDate):
alsobog
=
AlsoBogus
()
self
.
assertRaises
(
ValueError
,
dt
.
astimezone
,
alsobog
)
# also naive
def
test_subclass_datetime
(
self
):
class
C
(
self
.
theclass
):
theAnswer
=
42
def
__new__
(
cls
,
*
args
,
**
kws
):
temp
=
kws
.
copy
()
extra
=
temp
.
pop
(
'extra'
)
result
=
self
.
theclass
.
__new__
(
cls
,
*
args
,
**
temp
)
result
.
extra
=
extra
return
result
def
newmeth
(
self
,
start
):
return
start
+
self
.
year
+
self
.
month
+
self
.
second
args
=
2003
,
4
,
14
,
12
,
13
,
41
dt1
=
self
.
theclass
(
*
args
)
dt2
=
C
(
*
args
,
**
{
'extra'
:
7
})
self
.
assertEqual
(
dt2
.
__class__
,
C
)
self
.
assertEqual
(
dt2
.
theAnswer
,
42
)
self
.
assertEqual
(
dt2
.
extra
,
7
)
self
.
assertEqual
(
dt1
.
toordinal
(),
dt2
.
toordinal
())
self
.
assertEqual
(
dt2
.
newmeth
(
-
7
),
dt1
.
year
+
dt1
.
month
+
dt1
.
second
-
7
)
class
TestTime
(
HarmlessMixedComparison
):
theclass
=
time
...
...
@@ -1660,6 +1682,32 @@ class TestTime(HarmlessMixedComparison):
self
.
assertRaises
(
ValueError
,
base
.
replace
,
second
=
100
)
self
.
assertRaises
(
ValueError
,
base
.
replace
,
microsecond
=
1000000
)
def
test_subclass_time
(
self
):
class
C
(
self
.
theclass
):
theAnswer
=
42
def
__new__
(
cls
,
*
args
,
**
kws
):
temp
=
kws
.
copy
()
extra
=
temp
.
pop
(
'extra'
)
result
=
self
.
theclass
.
__new__
(
cls
,
*
args
,
**
temp
)
result
.
extra
=
extra
return
result
def
newmeth
(
self
,
start
):
return
start
+
self
.
hour
+
self
.
second
args
=
4
,
5
,
6
dt1
=
self
.
theclass
(
*
args
)
dt2
=
C
(
*
args
,
**
{
'extra'
:
7
})
self
.
assertEqual
(
dt2
.
__class__
,
C
)
self
.
assertEqual
(
dt2
.
theAnswer
,
42
)
self
.
assertEqual
(
dt2
.
extra
,
7
)
self
.
assertEqual
(
dt1
.
isoformat
(),
dt2
.
isoformat
())
self
.
assertEqual
(
dt2
.
newmeth
(
-
7
),
dt1
.
hour
+
dt1
.
second
-
7
)
# A mixin for classes with a tzinfo= argument. Subclasses must define
# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
# must be legit (which is true for time and datetime).
...
...
@@ -2042,6 +2090,32 @@ class TestTimeTZ(TestTime, TZInfoBase):
t2
=
t2
.
replace
(
tzinfo
=
Varies
())
self
.
failUnless
(
t1
<
t2
)
# t1's offset counter still going up
def
test_subclass_timetz
(
self
):
class
C
(
self
.
theclass
):
theAnswer
=
42
def
__new__
(
cls
,
*
args
,
**
kws
):
temp
=
kws
.
copy
()
extra
=
temp
.
pop
(
'extra'
)
result
=
self
.
theclass
.
__new__
(
cls
,
*
args
,
**
temp
)
result
.
extra
=
extra
return
result
def
newmeth
(
self
,
start
):
return
start
+
self
.
hour
+
self
.
second
args
=
4
,
5
,
6
,
500
,
FixedOffset
(
-
300
,
"EST"
,
1
)
dt1
=
self
.
theclass
(
*
args
)
dt2
=
C
(
*
args
,
**
{
'extra'
:
7
})
self
.
assertEqual
(
dt2
.
__class__
,
C
)
self
.
assertEqual
(
dt2
.
theAnswer
,
42
)
self
.
assertEqual
(
dt2
.
extra
,
7
)
self
.
assertEqual
(
dt1
.
utcoffset
(),
dt2
.
utcoffset
())
self
.
assertEqual
(
dt2
.
newmeth
(
-
7
),
dt1
.
hour
+
dt1
.
second
-
7
)
# Testing datetime objects with a non-None tzinfo.
...
...
@@ -2625,6 +2699,32 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase):
t2
=
t2
.
replace
(
tzinfo
=
Varies
())
self
.
failUnless
(
t1
<
t2
)
# t1's offset counter still going up
def
test_subclass_datetimetz
(
self
):
class
C
(
self
.
theclass
):
theAnswer
=
42
def
__new__
(
cls
,
*
args
,
**
kws
):
temp
=
kws
.
copy
()
extra
=
temp
.
pop
(
'extra'
)
result
=
self
.
theclass
.
__new__
(
cls
,
*
args
,
**
temp
)
result
.
extra
=
extra
return
result
def
newmeth
(
self
,
start
):
return
start
+
self
.
hour
+
self
.
year
args
=
2002
,
12
,
31
,
4
,
5
,
6
,
500
,
FixedOffset
(
-
300
,
"EST"
,
1
)
dt1
=
self
.
theclass
(
*
args
)
dt2
=
C
(
*
args
,
**
{
'extra'
:
7
})
self
.
assertEqual
(
dt2
.
__class__
,
C
)
self
.
assertEqual
(
dt2
.
theAnswer
,
42
)
self
.
assertEqual
(
dt2
.
extra
,
7
)
self
.
assertEqual
(
dt1
.
utcoffset
(),
dt2
.
utcoffset
())
self
.
assertEqual
(
dt2
.
newmeth
(
-
7
),
dt1
.
hour
+
dt1
.
year
-
7
)
# Pain to set up DST-aware tzinfo classes.
def
first_sunday_on_or_after
(
dt
):
...
...
Misc/NEWS
View file @
a98924a0
...
...
@@ -26,6 +26,9 @@ Core and builtins
Extension
modules
-----------------
-
The
datetime
.
datetime
and
datetime
.
time
classes
are
now
properly
subclassable
.
-
_tkinter
.{
get
|
set
}
busywaitinterval
was
added
.
-
itertools
.
islice
()
now
accepts
stop
=
None
as
documented
.
...
...
Modules/datetimemodule.c
View file @
a98924a0
...
...
@@ -1235,38 +1235,42 @@ cmperror(PyObject *a, PyObject *b)
}
/* ---------------------------------------------------------------------------
* Basic object allocation. These allocate Python objects of the right
* size and type, and do the Python object-initialization bit. If there's
* not enough memory, they return NULL after setting MemoryError. All
* data members remain uninitialized trash.
* Basic object allocation: tp_alloc implementatiosn. These allocate
* Python objects of the right size and type, and do the Python object-
* initialization bit. If there's not enough memory, they return NULL after
* setting MemoryError. All data members remain uninitialized trash.
*
* We abuse the tp_alloc "nitems" argument to communicate whether a tzinfo
* member is needed. This is ugly.
*/
static
PyDateTime_Time
*
alloc_time
(
int
aware
)
static
PyObject
*
time_alloc
(
PyTypeObject
*
type
,
int
aware
)
{
Py
DateTime_Time
*
self
;
Py
Object
*
self
;
self
=
(
Py
DateTime_Time
*
)
self
=
(
Py
Object
*
)
PyObject_MALLOC
(
aware
?
sizeof
(
PyDateTime_Time
)
:
sizeof
(
_PyDateTime_BaseTime
));
if
(
self
==
NULL
)
return
(
Py
DateTime_Time
*
)
PyErr_NoMemory
();
PyObject_INIT
(
self
,
&
PyDateTime_TimeT
ype
);
return
(
Py
Object
*
)
PyErr_NoMemory
();
PyObject_INIT
(
self
,
t
ype
);
return
self
;
}
static
Py
DateTime_DateTime
*
alloc_datetime
(
int
aware
)
static
Py
Object
*
datetime_alloc
(
PyTypeObject
*
type
,
int
aware
)
{
Py
DateTime_DateTime
*
self
;
Py
Object
*
self
;
self
=
(
Py
DateTime_DateTime
*
)
self
=
(
Py
Object
*
)
PyObject_MALLOC
(
aware
?
sizeof
(
PyDateTime_DateTime
)
:
sizeof
(
_PyDateTime_BaseDateTime
));
if
(
self
==
NULL
)
return
(
Py
DateTime_DateTime
*
)
PyErr_NoMemory
();
PyObject_INIT
(
self
,
&
PyDateTime_DateTimeT
ype
);
return
(
Py
Object
*
)
PyErr_NoMemory
();
PyObject_INIT
(
self
,
t
ype
);
return
self
;
}
...
...
@@ -1302,17 +1306,17 @@ new_date_ex(int year, int month, int day, PyTypeObject *type)
}
#define new_date(year, month, day) \
(new_date_ex(year, month, day, &PyDateTime_DateType)
)
new_date_ex(year, month, day, &PyDateTime_DateType
)
/* Create a datetime instance with no range checking. */
static
PyObject
*
new_datetime
(
int
year
,
int
month
,
int
day
,
int
hour
,
int
minute
,
int
second
,
int
usecond
,
PyObject
*
tzinfo
)
new_datetime
_ex
(
int
year
,
int
month
,
int
day
,
int
hour
,
int
minute
,
int
second
,
int
usecond
,
PyObject
*
tzinfo
,
PyTypeObject
*
type
)
{
PyDateTime_DateTime
*
self
;
char
aware
=
tzinfo
!=
Py_None
;
self
=
alloc_datetime
(
aware
);
self
=
(
PyDateTime_DateTime
*
)
(
type
->
tp_alloc
(
type
,
aware
)
);
if
(
self
!=
NULL
)
{
self
->
hastzinfo
=
aware
;
set_date_fields
((
PyDateTime_Date
*
)
self
,
year
,
month
,
day
);
...
...
@@ -1328,14 +1332,19 @@ new_datetime(int year, int month, int day, int hour, int minute,
return
(
PyObject
*
)
self
;
}
#define new_datetime(y, m, d, hh, mm, ss, us, tzinfo) \
new_datetime_ex(y, m, d, hh, mm, ss, us, tzinfo, \
&PyDateTime_DateTimeType)
/* Create a time instance with no range checking. */
static
PyObject
*
new_time
(
int
hour
,
int
minute
,
int
second
,
int
usecond
,
PyObject
*
tzinfo
)
new_time_ex
(
int
hour
,
int
minute
,
int
second
,
int
usecond
,
PyObject
*
tzinfo
,
PyTypeObject
*
type
)
{
PyDateTime_Time
*
self
;
char
aware
=
tzinfo
!=
Py_None
;
self
=
alloc_time
(
aware
);
self
=
(
PyDateTime_Time
*
)
(
type
->
tp_alloc
(
type
,
aware
)
);
if
(
self
!=
NULL
)
{
self
->
hastzinfo
=
aware
;
self
->
hashcode
=
-
1
;
...
...
@@ -1351,6 +1360,9 @@ new_time(int hour, int minute, int second, int usecond, PyObject *tzinfo)
return
(
PyObject
*
)
self
;
}
#define new_time(hh, mm, ss, us, tzinfo) \
new_time_ex(hh, mm, ss, us, tzinfo, &PyDateTime_TimeType)
/* Create a timedelta instance. Normalize the members iff normalize is
* true. Passing false is a speed optimization, if you know for sure
* that seconds and microseconds are already in their proper ranges. In any
...
...
@@ -3014,7 +3026,8 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
}
}
aware
=
(
char
)(
tzinfo
!=
Py_None
);
me
=
alloc_time
(
aware
);
me
=
(
PyDateTime_Time
*
)
time_alloc
(
&
PyDateTime_TimeType
,
aware
);
if
(
me
!=
NULL
)
{
char
*
pdata
=
PyString_AS_STRING
(
state
);
...
...
@@ -3036,7 +3049,8 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
return
NULL
;
if
(
check_tzinfo_subclass
(
tzinfo
)
<
0
)
return
NULL
;
self
=
new_time
(
hour
,
minute
,
second
,
usecond
,
tzinfo
);
self
=
new_time_ex
(
hour
,
minute
,
second
,
usecond
,
tzinfo
,
type
);
}
return
self
;
}
...
...
@@ -3439,7 +3453,7 @@ statichere PyTypeObject PyDateTime_TimeType = {
0
,
/* tp_descr_set */
0
,
/* tp_dictoffset */
0
,
/* tp_init */
0
,
/* tp_alloc */
time_alloc
,
/* tp_alloc */
time_new
,
/* tp_new */
0
,
/* tp_free */
};
...
...
@@ -3534,7 +3548,9 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
}
}
aware
=
(
char
)(
tzinfo
!=
Py_None
);
me
=
alloc_datetime
(
aware
);
me
=
(
PyDateTime_DateTime
*
)
datetime_alloc
(
&
PyDateTime_DateTimeType
,
aware
);
if
(
me
!=
NULL
)
{
char
*
pdata
=
PyString_AS_STRING
(
state
);
...
...
@@ -3558,9 +3574,9 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
return
NULL
;
if
(
check_tzinfo_subclass
(
tzinfo
)
<
0
)
return
NULL
;
self
=
new_datetime
(
year
,
month
,
day
,
hour
,
minute
,
second
,
usecond
,
tzinfo
);
self
=
new_datetime
_ex
(
year
,
month
,
day
,
hour
,
minute
,
second
,
usecond
,
tzinfo
,
type
);
}
return
self
;
}
...
...
@@ -4460,7 +4476,7 @@ statichere PyTypeObject PyDateTime_DateTimeType = {
0
,
/* tp_descr_set */
0
,
/* tp_dictoffset */
0
,
/* tp_init */
0
,
/* tp_alloc */
datetime_alloc
,
/* tp_alloc */
datetime_new
,
/* tp_new */
0
,
/* tp_free */
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment