diff --git a/Doc/includes/tzinfo_examples.py b/Doc/includes/tzinfo_examples.py
index 1fa6e615e46a76..762b1b62fc871d 100644
--- a/Doc/includes/tzinfo_examples.py
+++ b/Doc/includes/tzinfo_examples.py
@@ -1,68 +1,70 @@
-from datetime import tzinfo, timedelta, datetime
-
-ZERO = timedelta(0)
-HOUR = timedelta(hours=1)
-SECOND = timedelta(seconds=1)
+import datetime as dt
# A class capturing the platform's idea of local time.
# (May result in wrong values on historical times in
# timezones where UTC offset and/or the DST rules had
# changed in the past.)
-import time as _time
+import time
+
+ZERO = dt.timedelta(0)
+HOUR = dt.timedelta(hours=1)
+SECOND = dt.timedelta(seconds=1)
-STDOFFSET = timedelta(seconds = -_time.timezone)
-if _time.daylight:
- DSTOFFSET = timedelta(seconds = -_time.altzone)
+STDOFFSET = dt.timedelta(seconds=-time.timezone)
+if time.daylight:
+ DSTOFFSET = dt.timedelta(seconds=-time.altzone)
else:
DSTOFFSET = STDOFFSET
DSTDIFF = DSTOFFSET - STDOFFSET
-class LocalTimezone(tzinfo):
- def fromutc(self, dt):
- assert dt.tzinfo is self
- stamp = (dt - datetime(1970, 1, 1, tzinfo=self)) // SECOND
- args = _time.localtime(stamp)[:6]
+class LocalTimezone(dt.tzinfo):
+
+ def fromutc(self, when):
+ assert when.tzinfo is self
+ stamp = (when - dt.datetime(1970, 1, 1, tzinfo=self)) // SECOND
+ args = time.localtime(stamp)[:6]
dst_diff = DSTDIFF // SECOND
# Detect fold
- fold = (args == _time.localtime(stamp - dst_diff))
- return datetime(*args, microsecond=dt.microsecond,
- tzinfo=self, fold=fold)
+ fold = (args == time.localtime(stamp - dst_diff))
+ return dt.datetime(*args, microsecond=when.microsecond,
+ tzinfo=self, fold=fold)
- def utcoffset(self, dt):
- if self._isdst(dt):
+ def utcoffset(self, when):
+ if self._isdst(when):
return DSTOFFSET
else:
return STDOFFSET
- def dst(self, dt):
- if self._isdst(dt):
+ def dst(self, when):
+ if self._isdst(when):
return DSTDIFF
else:
return ZERO
- def tzname(self, dt):
- return _time.tzname[self._isdst(dt)]
+ def tzname(self, when):
+ return time.tzname[self._isdst(when)]
- def _isdst(self, dt):
- tt = (dt.year, dt.month, dt.day,
- dt.hour, dt.minute, dt.second,
- dt.weekday(), 0, 0)
- stamp = _time.mktime(tt)
- tt = _time.localtime(stamp)
+ def _isdst(self, when):
+ tt = (when.year, when.month, when.day,
+ when.hour, when.minute, when.second,
+ when.weekday(), 0, 0)
+ stamp = time.mktime(tt)
+ tt = time.localtime(stamp)
return tt.tm_isdst > 0
+
Local = LocalTimezone()
# A complete implementation of current DST rules for major US time zones.
-def first_sunday_on_or_after(dt):
- days_to_go = 6 - dt.weekday()
+def first_sunday_on_or_after(when):
+ days_to_go = 6 - when.weekday()
if days_to_go:
- dt += timedelta(days_to_go)
- return dt
+ when += dt.timedelta(days_to_go)
+ return when
# US DST Rules
@@ -75,21 +77,22 @@ def first_sunday_on_or_after(dt):
#
# In the US, since 2007, DST starts at 2am (standard time) on the second
# Sunday in March, which is the first Sunday on or after Mar 8.
-DSTSTART_2007 = datetime(1, 3, 8, 2)
+DSTSTART_2007 = dt.datetime(1, 3, 8, 2)
# and ends at 2am (DST time) on the first Sunday of Nov.
-DSTEND_2007 = datetime(1, 11, 1, 2)
+DSTEND_2007 = dt.datetime(1, 11, 1, 2)
# From 1987 to 2006, DST used to start at 2am (standard time) on the first
# Sunday in April and to end at 2am (DST time) on the last
# Sunday of October, which is the first Sunday on or after Oct 25.
-DSTSTART_1987_2006 = datetime(1, 4, 1, 2)
-DSTEND_1987_2006 = datetime(1, 10, 25, 2)
+DSTSTART_1987_2006 = dt.datetime(1, 4, 1, 2)
+DSTEND_1987_2006 = dt.datetime(1, 10, 25, 2)
# From 1967 to 1986, DST used to start at 2am (standard time) on the last
# Sunday in April (the one on or after April 24) and to end at 2am (DST time)
# on the last Sunday of October, which is the first Sunday
# on or after Oct 25.
-DSTSTART_1967_1986 = datetime(1, 4, 24, 2)
+DSTSTART_1967_1986 = dt.datetime(1, 4, 24, 2)
DSTEND_1967_1986 = DSTEND_1987_2006
+
def us_dst_range(year):
# Find start and end times for US DST. For years before 1967, return
# start = end for no DST.
@@ -100,17 +103,17 @@ def us_dst_range(year):
elif 1966 < year < 1987:
dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986
else:
- return (datetime(year, 1, 1), ) * 2
+ return (dt.datetime(year, 1, 1), ) * 2
start = first_sunday_on_or_after(dststart.replace(year=year))
end = first_sunday_on_or_after(dstend.replace(year=year))
return start, end
-class USTimeZone(tzinfo):
+class USTimeZone(dt.tzinfo):
def __init__(self, hours, reprname, stdname, dstname):
- self.stdoffset = timedelta(hours=hours)
+ self.stdoffset = dt.timedelta(hours=hours)
self.reprname = reprname
self.stdname = stdname
self.dstname = dstname
@@ -118,45 +121,45 @@ def __init__(self, hours, reprname, stdname, dstname):
def __repr__(self):
return self.reprname
- def tzname(self, dt):
- if self.dst(dt):
+ def tzname(self, when):
+ if self.dst(when):
return self.dstname
else:
return self.stdname
- def utcoffset(self, dt):
- return self.stdoffset + self.dst(dt)
+ def utcoffset(self, when):
+ return self.stdoffset + self.dst(when)
- def dst(self, dt):
- if dt is None or dt.tzinfo is None:
+ def dst(self, when):
+ if when is None or when.tzinfo is None:
# An exception may be sensible here, in one or both cases.
# It depends on how you want to treat them. The default
# fromutc() implementation (called by the default astimezone()
- # implementation) passes a datetime with dt.tzinfo is self.
+ # implementation) passes a datetime with when.tzinfo is self.
return ZERO
- assert dt.tzinfo is self
- start, end = us_dst_range(dt.year)
+ assert when.tzinfo is self
+ start, end = us_dst_range(when.year)
# Can't compare naive to aware objects, so strip the timezone from
- # dt first.
- dt = dt.replace(tzinfo=None)
- if start + HOUR <= dt < end - HOUR:
+ # when first.
+ when = when.replace(tzinfo=None)
+ if start + HOUR <= when < end - HOUR:
# DST is in effect.
return HOUR
- if end - HOUR <= dt < end:
- # Fold (an ambiguous hour): use dt.fold to disambiguate.
- return ZERO if dt.fold else HOUR
- if start <= dt < start + HOUR:
+ if end - HOUR <= when < end:
+ # Fold (an ambiguous hour): use when.fold to disambiguate.
+ return ZERO if when.fold else HOUR
+ if start <= when < start + HOUR:
# Gap (a non-existent hour): reverse the fold rule.
- return HOUR if dt.fold else ZERO
+ return HOUR if when.fold else ZERO
# DST is off.
return ZERO
- def fromutc(self, dt):
- assert dt.tzinfo is self
- start, end = us_dst_range(dt.year)
+ def fromutc(self, when):
+ assert when.tzinfo is self
+ start, end = us_dst_range(when.year)
start = start.replace(tzinfo=self)
end = end.replace(tzinfo=self)
- std_time = dt + self.stdoffset
+ std_time = when + self.stdoffset
dst_time = std_time + HOUR
if end <= dst_time < end + HOUR:
# Repeated hour
diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index ebe3c3576c0979..73217136f14472 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -230,8 +230,8 @@ A :class:`timedelta` object represents a duration, the difference between two
*days*, *seconds* and *microseconds* are "merged" and normalized into those
three resulting attributes::
- >>> from datetime import timedelta
- >>> delta = timedelta(
+ >>> import datetime as dt
+ >>> delta = dt.timedelta(
... days=50,
... seconds=27,
... microseconds=10,
@@ -244,6 +244,12 @@ A :class:`timedelta` object represents a duration, the difference between two
>>> delta
datetime.timedelta(days=64, seconds=29156, microseconds=10)
+ .. tip::
+ ``import datetime as dt`` instead of ``import datetime`` or
+ ``from datetime import datetime`` to avoid confusion between the module
+ and the class. See `How I Import Python’s datetime Module
+ `__.
+
If any argument is a float and there are fractional microseconds,
the fractional microseconds left over from all arguments are
combined and their sum is rounded to the nearest microsecond using
@@ -257,8 +263,8 @@ A :class:`timedelta` object represents a duration, the difference between two
Note that normalization of negative values may be surprising at first. For
example::
- >>> from datetime import timedelta
- >>> d = timedelta(microseconds=-1)
+ >>> import datetime as dt
+ >>> d = dt.timedelta(microseconds=-1)
>>> (d.days, d.seconds, d.microseconds)
(-1, 86399, 999999)
@@ -321,8 +327,8 @@ Instance attributes (read-only):
.. doctest::
- >>> from datetime import timedelta
- >>> duration = timedelta(seconds=11235813)
+ >>> import datetime as dt
+ >>> duration = dt.timedelta(seconds=11235813)
>>> duration.days, duration.seconds
(130, 3813)
>>> duration.total_seconds()
@@ -461,10 +467,10 @@ Examples of usage: :class:`!timedelta`
An additional example of normalization::
>>> # Components of another_year add up to exactly 365 days
- >>> from datetime import timedelta
- >>> year = timedelta(days=365)
- >>> another_year = timedelta(weeks=40, days=84, hours=23,
- ... minutes=50, seconds=600)
+ >>> import datetime as dt
+ >>> year = dt.timedelta(days=365)
+ >>> another_year = dt.timedelta(weeks=40, days=84, hours=23,
+ ... minutes=50, seconds=600)
>>> year == another_year
True
>>> year.total_seconds()
@@ -472,8 +478,8 @@ An additional example of normalization::
Examples of :class:`timedelta` arithmetic::
- >>> from datetime import timedelta
- >>> year = timedelta(days=365)
+ >>> import datetime as dt
+ >>> year = dt.timedelta(days=365)
>>> ten_years = 10 * year
>>> ten_years
datetime.timedelta(days=3650)
@@ -565,12 +571,12 @@ Other constructors, all class methods:
Examples::
- >>> from datetime import date
- >>> date.fromisoformat('2019-12-04')
+ >>> import datetime as dt
+ >>> dt.date.fromisoformat('2019-12-04')
datetime.date(2019, 12, 4)
- >>> date.fromisoformat('20191204')
+ >>> dt.date.fromisoformat('20191204')
datetime.date(2019, 12, 4)
- >>> date.fromisoformat('2021-W01-1')
+ >>> dt.date.fromisoformat('2021-W01-1')
datetime.date(2021, 1, 4)
.. versionadded:: 3.7
@@ -611,9 +617,9 @@ Other constructors, all class methods:
.. doctest::
- >>> from datetime import date
+ >>> import datetime as dt
>>> date_string = "02/29"
- >>> when = date.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug.
+ >>> when = dt.date.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug.
>>> when.strftime("%B %d") # doctest: +SKIP
'February 29'
@@ -728,8 +734,8 @@ Instance methods:
Example::
- >>> from datetime import date
- >>> d = date(2002, 12, 31)
+ >>> import datetime as dt
+ >>> d = dt.date(2002, 12, 31)
>>> d.replace(day=26)
datetime.date(2002, 12, 26)
@@ -787,10 +793,10 @@ Instance methods:
For example, 2004 begins on a Thursday, so the first week of ISO year 2004
begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004::
- >>> from datetime import date
- >>> date(2003, 12, 29).isocalendar()
+ >>> import datetime as dt
+ >>> dt.date(2003, 12, 29).isocalendar()
datetime.IsoCalendarDate(year=2004, week=1, weekday=1)
- >>> date(2004, 1, 4).isocalendar()
+ >>> dt.date(2004, 1, 4).isocalendar()
datetime.IsoCalendarDate(year=2004, week=1, weekday=7)
.. versionchanged:: 3.9
@@ -801,8 +807,8 @@ Instance methods:
Return a string representing the date in ISO 8601 format, ``YYYY-MM-DD``::
- >>> from datetime import date
- >>> date(2002, 12, 4).isoformat()
+ >>> import datetime as dt
+ >>> dt.date(2002, 12, 4).isoformat()
'2002-12-04'
@@ -815,8 +821,8 @@ Instance methods:
Return a string representing the date::
- >>> from datetime import date
- >>> date(2002, 12, 4).ctime()
+ >>> import datetime as dt
+ >>> dt.date(2002, 12, 4).ctime()
'Wed Dec 4 00:00:00 2002'
``d.ctime()`` is equivalent to::
@@ -849,13 +855,13 @@ Examples of usage: :class:`!date`
Example of counting days to an event::
>>> import time
- >>> from datetime import date
- >>> today = date.today()
+ >>> import datetime as dt
+ >>> today = dt.date.today()
>>> today
datetime.date(2007, 12, 5)
- >>> today == date.fromtimestamp(time.time())
+ >>> today == dt.date.fromtimestamp(time.time())
True
- >>> my_birthday = date(today.year, 6, 24)
+ >>> my_birthday = dt.date(today.year, 6, 24)
>>> if my_birthday < today:
... my_birthday = my_birthday.replace(year=today.year + 1)
...
@@ -869,8 +875,8 @@ More examples of working with :class:`date`:
.. doctest::
- >>> from datetime import date
- >>> d = date.fromordinal(730920) # 730920th day after 1. 1. 0001
+ >>> import datetime as dt
+ >>> d = dt.date.fromordinal(730920) # 730920th day after 1. 1. 0001
>>> d
datetime.date(2002, 3, 11)
@@ -1123,24 +1129,24 @@ Other constructors, all class methods:
Examples::
- >>> from datetime import datetime
- >>> datetime.fromisoformat('2011-11-04')
+ >>> import datetime as dt
+ >>> dt.datetime.fromisoformat('2011-11-04')
datetime.datetime(2011, 11, 4, 0, 0)
- >>> datetime.fromisoformat('20111104')
+ >>> dt.datetime.fromisoformat('20111104')
datetime.datetime(2011, 11, 4, 0, 0)
- >>> datetime.fromisoformat('2011-11-04T00:05:23')
+ >>> dt.datetime.fromisoformat('2011-11-04T00:05:23')
datetime.datetime(2011, 11, 4, 0, 5, 23)
- >>> datetime.fromisoformat('2011-11-04T00:05:23Z')
+ >>> dt.datetime.fromisoformat('2011-11-04T00:05:23Z')
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone.utc)
- >>> datetime.fromisoformat('20111104T000523')
+ >>> dt.datetime.fromisoformat('20111104T000523')
datetime.datetime(2011, 11, 4, 0, 5, 23)
- >>> datetime.fromisoformat('2011-W01-2T00:05:23.283')
+ >>> dt.datetime.fromisoformat('2011-W01-2T00:05:23.283')
datetime.datetime(2011, 1, 4, 0, 5, 23, 283000)
- >>> datetime.fromisoformat('2011-11-04 00:05:23.283')
+ >>> dt.datetime.fromisoformat('2011-11-04 00:05:23.283')
datetime.datetime(2011, 11, 4, 0, 5, 23, 283000)
- >>> datetime.fromisoformat('2011-11-04 00:05:23.283+00:00')
+ >>> dt.datetime.fromisoformat('2011-11-04 00:05:23.283+00:00')
datetime.datetime(2011, 11, 4, 0, 5, 23, 283000, tzinfo=datetime.timezone.utc)
- >>> datetime.fromisoformat('2011-11-04T00:05:23+04:00') # doctest: +NORMALIZE_WHITESPACE
+ >>> dt.datetime.fromisoformat('2011-11-04T00:05:23+04:00') # doctest: +NORMALIZE_WHITESPACE
datetime.datetime(2011, 11, 4, 0, 5, 23,
tzinfo=datetime.timezone(datetime.timedelta(seconds=14400)))
@@ -1187,9 +1193,9 @@ Other constructors, all class methods:
.. doctest::
- >>> from datetime import datetime
+ >>> import datetime as dt
>>> date_string = "02/29"
- >>> when = datetime.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug.
+ >>> when = dt.datetime.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug.
>>> when.strftime("%B %d") # doctest: +SKIP
'February 29'
@@ -1599,24 +1605,24 @@ Instance methods:
Examples::
- >>> from datetime import datetime, timezone
- >>> datetime(2019, 5, 18, 15, 17, 8, 132263).isoformat()
+ >>> import datetime as dt
+ >>> dt.datetime(2019, 5, 18, 15, 17, 8, 132263).isoformat()
'2019-05-18T15:17:08.132263'
- >>> datetime(2019, 5, 18, 15, 17, tzinfo=timezone.utc).isoformat()
+ >>> dt.datetime(2019, 5, 18, 15, 17, tzinfo=dt.timezone.utc).isoformat()
'2019-05-18T15:17:00+00:00'
The optional argument *sep* (default ``'T'``) is a one-character separator,
placed between the date and time portions of the result. For example::
- >>> from datetime import tzinfo, timedelta, datetime
- >>> class TZ(tzinfo):
+ >>> import datetime as dt
+ >>> class TZ(dt.tzinfo):
... """A time zone with an arbitrary, constant -06:39 offset."""
- ... def utcoffset(self, dt):
- ... return timedelta(hours=-6, minutes=-39)
+ ... def utcoffset(self, when):
+ ... return dt.timedelta(hours=-6, minutes=-39)
...
- >>> datetime(2002, 12, 25, tzinfo=TZ()).isoformat(' ')
+ >>> dt.datetime(2002, 12, 25, tzinfo=TZ()).isoformat(' ')
'2002-12-25 00:00:00-06:39'
- >>> datetime(2009, 11, 27, microsecond=100, tzinfo=TZ()).isoformat()
+ >>> dt.datetime(2009, 11, 27, microsecond=100, tzinfo=TZ()).isoformat()
'2009-11-27T00:00:00.000100-06:39'
The optional argument *timespec* specifies the number of additional
@@ -1640,11 +1646,11 @@ Instance methods:
:exc:`ValueError` will be raised on an invalid *timespec* argument::
- >>> from datetime import datetime
- >>> datetime.now().isoformat(timespec='minutes') # doctest: +SKIP
+ >>> import datetime as dt
+ >>> dt.datetime.now().isoformat(timespec='minutes') # doctest: +SKIP
'2002-12-25T00:00'
- >>> dt = datetime(2015, 1, 1, 12, 30, 59, 0)
- >>> dt.isoformat(timespec='microseconds')
+ >>> my_datetime = dt.datetime(2015, 1, 1, 12, 30, 59, 0)
+ >>> my_datetime.isoformat(timespec='microseconds')
'2015-01-01T12:30:59.000000'
.. versionchanged:: 3.6
@@ -1661,8 +1667,8 @@ Instance methods:
Return a string representing the date and time::
- >>> from datetime import datetime
- >>> datetime(2002, 12, 4, 20, 30, 40).ctime()
+ >>> import datetime as dt
+ >>> dt.datetime(2002, 12, 4, 20, 30, 40).ctime()
'Wed Dec 4 20:30:40 2002'
The output string will *not* include time zone information, regardless
@@ -1699,27 +1705,27 @@ Examples of working with :class:`.datetime` objects:
.. doctest::
- >>> from datetime import datetime, date, time, timezone
+ >>> import datetime as dt
>>> # Using datetime.combine()
- >>> d = date(2005, 7, 14)
- >>> t = time(12, 30)
- >>> datetime.combine(d, t)
+ >>> d = dt.date(2005, 7, 14)
+ >>> t = dt.time(12, 30)
+ >>> dt.datetime.combine(d, t)
datetime.datetime(2005, 7, 14, 12, 30)
>>> # Using datetime.now()
- >>> datetime.now() # doctest: +SKIP
+ >>> dt.datetime.now() # doctest: +SKIP
datetime.datetime(2007, 12, 6, 16, 29, 43, 79043) # GMT +1
- >>> datetime.now(timezone.utc) # doctest: +SKIP
+ >>> dt.datetime.now(dt.timezone.utc) # doctest: +SKIP
datetime.datetime(2007, 12, 6, 15, 29, 43, 79060, tzinfo=datetime.timezone.utc)
>>> # Using datetime.strptime()
- >>> dt = datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M")
- >>> dt
+ >>> my_datetime = dt.datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M")
+ >>> my_datetime
datetime.datetime(2006, 11, 21, 16, 30)
>>> # Using datetime.timetuple() to get tuple of all attributes
- >>> tt = dt.timetuple()
+ >>> tt = my_datetime.timetuple()
>>> for it in tt: # doctest: +SKIP
... print(it)
...
@@ -1734,7 +1740,7 @@ Examples of working with :class:`.datetime` objects:
-1 # dst - method tzinfo.dst() returned None
>>> # Date in ISO format
- >>> ic = dt.isocalendar()
+ >>> ic = my_datetime.isocalendar()
>>> for it in ic: # doctest: +SKIP
... print(it)
...
@@ -1743,55 +1749,55 @@ Examples of working with :class:`.datetime` objects:
2 # ISO weekday
>>> # Formatting a datetime
- >>> dt.strftime("%A, %d. %B %Y %I:%M%p")
+ >>> my_datetime.strftime("%A, %d. %B %Y %I:%M%p")
'Tuesday, 21. November 2006 04:30PM'
- >>> 'The {1} is {0:%d}, the {2} is {0:%B}, the {3} is {0:%I:%M%p}.'.format(dt, "day", "month", "time")
+ >>> 'The {1} is {0:%d}, the {2} is {0:%B}, the {3} is {0:%I:%M%p}.'.format(my_datetime, "day", "month", "time")
'The day is 21, the month is November, the time is 04:30PM.'
The example below defines a :class:`tzinfo` subclass capturing time zone
information for Kabul, Afghanistan, which used +4 UTC until 1945
and then +4:30 UTC thereafter::
- from datetime import timedelta, datetime, tzinfo, timezone
+ import datetime as dt
- class KabulTz(tzinfo):
+ class KabulTz(dt.tzinfo):
# Kabul used +4 until 1945, when they moved to +4:30
- UTC_MOVE_DATE = datetime(1944, 12, 31, 20, tzinfo=timezone.utc)
+ UTC_MOVE_DATE = dt.datetime(1944, 12, 31, 20, tzinfo=dt.timezone.utc)
- def utcoffset(self, dt):
- if dt.year < 1945:
- return timedelta(hours=4)
- elif (1945, 1, 1, 0, 0) <= dt.timetuple()[:5] < (1945, 1, 1, 0, 30):
+ def utcoffset(self, when):
+ if when.year < 1945:
+ return dt.timedelta(hours=4)
+ elif (1945, 1, 1, 0, 0) <= when.timetuple()[:5] < (1945, 1, 1, 0, 30):
# An ambiguous ("imaginary") half-hour range representing
# a 'fold' in time due to the shift from +4 to +4:30.
- # If dt falls in the imaginary range, use fold to decide how
- # to resolve. See PEP495.
- return timedelta(hours=4, minutes=(30 if dt.fold else 0))
+ # If when falls in the imaginary range, use fold to decide how
+ # to resolve. See PEP 495.
+ return dt.timedelta(hours=4, minutes=(30 if when.fold else 0))
else:
- return timedelta(hours=4, minutes=30)
+ return dt.timedelta(hours=4, minutes=30)
- def fromutc(self, dt):
+ def fromutc(self, when):
# Follow same validations as in datetime.tzinfo
- if not isinstance(dt, datetime):
+ if not isinstance(when, dt.datetime):
raise TypeError("fromutc() requires a datetime argument")
- if dt.tzinfo is not self:
- raise ValueError("dt.tzinfo is not self")
+ if when.tzinfo is not self:
+ raise ValueError("when.tzinfo is not self")
# A custom implementation is required for fromutc as
# the input to this function is a datetime with utc values
# but with a tzinfo set to self.
# See datetime.astimezone or fromtimestamp.
- if dt.replace(tzinfo=timezone.utc) >= self.UTC_MOVE_DATE:
- return dt + timedelta(hours=4, minutes=30)
+ if when.replace(tzinfo=dt.timezone.utc) >= self.UTC_MOVE_DATE:
+ return when + dt.timedelta(hours=4, minutes=30)
else:
- return dt + timedelta(hours=4)
+ return when + dt.timedelta(hours=4)
- def dst(self, dt):
+ def dst(self, when):
# Kabul does not observe daylight saving time.
- return timedelta(0)
+ return dt.timedelta(0)
- def tzname(self, dt):
- if dt >= self.UTC_MOVE_DATE:
+ def tzname(self, when):
+ if when >= self.UTC_MOVE_DATE:
return "+04:30"
return "+04"
@@ -1800,17 +1806,17 @@ Usage of ``KabulTz`` from above::
>>> tz1 = KabulTz()
>>> # Datetime before the change
- >>> dt1 = datetime(1900, 11, 21, 16, 30, tzinfo=tz1)
+ >>> dt1 = dt.datetime(1900, 11, 21, 16, 30, tzinfo=tz1)
>>> print(dt1.utcoffset())
4:00:00
>>> # Datetime after the change
- >>> dt2 = datetime(2006, 6, 14, 13, 0, tzinfo=tz1)
+ >>> dt2 = dt.datetime(2006, 6, 14, 13, 0, tzinfo=tz1)
>>> print(dt2.utcoffset())
4:30:00
>>> # Convert datetime to another time zone
- >>> dt3 = dt2.astimezone(timezone.utc)
+ >>> dt3 = dt2.astimezone(dt.timezone.utc)
>>> dt3
datetime.datetime(2006, 6, 14, 8, 30, tzinfo=datetime.timezone.utc)
>>> dt2
@@ -1946,22 +1952,22 @@ Other constructors:
.. doctest::
- >>> from datetime import time
- >>> time.fromisoformat('04:23:01')
+ >>> import datetime as dt
+ >>> dt.time.fromisoformat('04:23:01')
datetime.time(4, 23, 1)
- >>> time.fromisoformat('T04:23:01')
+ >>> dt.time.fromisoformat('T04:23:01')
datetime.time(4, 23, 1)
- >>> time.fromisoformat('T042301')
+ >>> dt.time.fromisoformat('T042301')
datetime.time(4, 23, 1)
- >>> time.fromisoformat('04:23:01.000384')
+ >>> dt.time.fromisoformat('04:23:01.000384')
datetime.time(4, 23, 1, 384)
- >>> time.fromisoformat('04:23:01,000384')
+ >>> dt.time.fromisoformat('04:23:01,000384')
datetime.time(4, 23, 1, 384)
- >>> time.fromisoformat('04:23:01+04:00')
+ >>> dt.time.fromisoformat('04:23:01+04:00')
datetime.time(4, 23, 1, tzinfo=datetime.timezone(datetime.timedelta(seconds=14400)))
- >>> time.fromisoformat('04:23:01Z')
+ >>> dt.time.fromisoformat('04:23:01Z')
datetime.time(4, 23, 1, tzinfo=datetime.timezone.utc)
- >>> time.fromisoformat('04:23:01+00:00')
+ >>> dt.time.fromisoformat('04:23:01+00:00')
datetime.time(4, 23, 1, tzinfo=datetime.timezone.utc)
@@ -2036,13 +2042,13 @@ Instance methods:
Example::
- >>> from datetime import time
- >>> time(hour=12, minute=34, second=56, microsecond=123456).isoformat(timespec='minutes')
+ >>> import datetime as dt
+ >>> dt.time(hour=12, minute=34, second=56, microsecond=123456).isoformat(timespec='minutes')
'12:34'
- >>> dt = time(hour=12, minute=34, second=56, microsecond=0)
- >>> dt.isoformat(timespec='microseconds')
+ >>> my_time = dt.time(hour=12, minute=34, second=56, microsecond=0)
+ >>> my_time.isoformat(timespec='microseconds')
'12:34:56.000000'
- >>> dt.isoformat(timespec='auto')
+ >>> my_time.isoformat(timespec='auto')
'12:34:56'
.. versionchanged:: 3.6
@@ -2100,18 +2106,18 @@ Examples of usage: :class:`!time`
Examples of working with a :class:`.time` object::
- >>> from datetime import time, tzinfo, timedelta
- >>> class TZ1(tzinfo):
- ... def utcoffset(self, dt):
- ... return timedelta(hours=1)
- ... def dst(self, dt):
- ... return timedelta(0)
- ... def tzname(self,dt):
+ >>> import datetime as dt
+ >>> class TZ1(dt.tzinfo):
+ ... def utcoffset(self, when):
+ ... return dt.timedelta(hours=1)
+ ... def dst(self, when):
+ ... return dt.timedelta(0)
+ ... def tzname(self, when):
... return "+01:00"
... def __repr__(self):
... return f"{self.__class__.__name__}()"
...
- >>> t = time(12, 10, 30, tzinfo=TZ1())
+ >>> t = dt.time(12, 10, 30, tzinfo=TZ1())
>>> t
datetime.time(12, 10, 30, tzinfo=TZ1())
>>> t.isoformat()
@@ -2219,21 +2225,25 @@ Examples of working with a :class:`.time` object::
Most implementations of :meth:`dst` will probably look like one of these two::
- def dst(self, dt):
+ import datetime as dt
+
+ def dst(self, when):
# a fixed-offset class: doesn't account for DST
- return timedelta(0)
+ return dt.timedelta(0)
or::
- def dst(self, dt):
+ import datetime as dt
+
+ def dst(self, when):
# Code to set dston and dstoff to the time zone's DST
- # transition times based on the input dt.year, and expressed
+ # transition times based on the input when.year, and expressed
# in standard local time.
- if dston <= dt.replace(tzinfo=None) < dstoff:
- return timedelta(hours=1)
+ if dston <= when.replace(tzinfo=None) < dstoff:
+ return dt.timedelta(hours=1)
else:
- return timedelta(0)
+ return dt.timedelta(0)
The default implementation of :meth:`dst` raises :exc:`NotImplementedError`.
@@ -2299,20 +2309,22 @@ There is one more :class:`tzinfo` method that a subclass may wish to override:
Skipping code for error cases, the default :meth:`fromutc` implementation acts
like::
- def fromutc(self, dt):
- # raise ValueError error if dt.tzinfo is not self
- dtoff = dt.utcoffset()
- dtdst = dt.dst()
+ import datetime as dt
+
+ def fromutc(self, when):
+ # raise ValueError error if when.tzinfo is not self
+ dtoff = when.utcoffset()
+ dtdst = when.dst()
# raise ValueError if dtoff is None or dtdst is None
delta = dtoff - dtdst # this is self's standard offset
if delta:
- dt += delta # convert to standard local time
- dtdst = dt.dst()
+ when += delta # convert to standard local time
+ dtdst = when.dst()
# raise ValueError if dtdst is None
if dtdst:
- return dt + dtdst
+ return when + dtdst
else:
- return dt
+ return when
In the following :download:`tzinfo_examples.py
<../includes/tzinfo_examples.py>` file there are some examples of
@@ -2339,9 +2351,9 @@ When DST starts (the "start" line), the local wall clock leaps from 1:59 to
``astimezone(Eastern)`` won't deliver a result with ``hour == 2`` on the day DST
begins. For example, at the Spring forward transition of 2016, we get::
- >>> from datetime import datetime, timezone
+ >>> import datetime as dt
>>> from tzinfo_examples import HOUR, Eastern
- >>> u0 = datetime(2016, 3, 13, 5, tzinfo=timezone.utc)
+ >>> u0 = dt.datetime(2016, 3, 13, 5, tzinfo=dt.timezone.utc)
>>> for i in range(4):
... u = u0 + i*HOUR
... t = u.astimezone(Eastern)
@@ -2364,7 +2376,9 @@ form 5:MM and 6:MM both map to 1:MM when converted to Eastern, but earlier times
have the :attr:`~.datetime.fold` attribute set to 0 and the later times have it set to 1.
For example, at the Fall back transition of 2016, we get::
- >>> u0 = datetime(2016, 11, 6, 4, tzinfo=timezone.utc)
+ >>> import datetime as dt
+ >>> from tzinfo_examples import HOUR, Eastern
+ >>> u0 = dt.datetime(2016, 11, 6, 4, tzinfo=dt.timezone.utc)
>>> for i in range(4):
... u = u0 + i*HOUR
... t = u.astimezone(Eastern)
@@ -2515,8 +2529,9 @@ versus :meth:`~.datetime.strptime`:
These methods accept format codes that can be used to parse and format dates::
- >>> datetime.strptime('31/01/22 23:59:59.999999',
- ... '%d/%m/%y %H:%M:%S.%f')
+ >>> import datetime as dt
+ >>> dt.datetime.strptime('31/01/22 23:59:59.999999',
+ ... '%d/%m/%y %H:%M:%S.%f')
datetime.datetime(2022, 1, 31, 23, 59, 59, 999999)
>>> _.strftime('%a %d %b %Y, %I:%M%p')
'Mon 31 Jan 2022, 11:59PM'
@@ -2745,13 +2760,13 @@ in the format string will be pulled from the default value.
.. doctest::
- >>> from datetime import datetime
+ >>> import datetime as dt
>>> value = "2/29"
- >>> datetime.strptime(value, "%m/%d")
+ >>> dt.datetime.strptime(value, "%m/%d")
Traceback (most recent call last):
...
ValueError: day 29 must be in range 1..28 for month 2 in year 1900
- >>> datetime.strptime(f"1904 {value}", "%Y %m/%d")
+ >>> dt.datetime.strptime(f"1904 {value}", "%Y %m/%d")
datetime.datetime(1904, 2, 29, 0, 0)
Using ``datetime.strptime(date_string, format)`` is equivalent to::
@@ -2897,7 +2912,7 @@ Notes:
.. doctest::
>>> month_day = "02/29"
- >>> datetime.strptime(f"{month_day};1984", "%m/%d;%Y") # No leap year bug.
+ >>> dt.datetime.strptime(f"{month_day};1984", "%m/%d;%Y") # No leap year bug.
datetime.datetime(1984, 2, 29, 0, 0)
.. deprecated-removed:: 3.13 3.15
@@ -2908,7 +2923,7 @@ Notes:
.. rubric:: Footnotes
-.. [#] If, that is, we ignore the effects of Relativity
+.. [#] If, that is, we ignore the effects of relativity.
.. [#] This matches the definition of the "proleptic Gregorian" calendar in
Dershowitz and Reingold's book *Calendrical Calculations*,
diff --git a/Doc/library/marshal.rst b/Doc/library/marshal.rst
index ed182ea24e8f3c..25902622b8730b 100644
--- a/Doc/library/marshal.rst
+++ b/Doc/library/marshal.rst
@@ -51,8 +51,9 @@ this module. The following types are supported:
* Strings (:class:`str`) and :class:`bytes`.
:term:`Bytes-like objects ` like :class:`bytearray` are
marshalled as :class:`!bytes`.
-* Containers: :class:`tuple`, :class:`list`, :class:`set`, :class:`frozenset`,
- and (since :data:`version` 5), :class:`slice`.
+* Containers: :class:`tuple`, :class:`list`, :class:`dict`, :class:`frozendict`
+ (since :data:`version` 6), :class:`set`, :class:`frozenset`, and
+ :class:`slice` (since :data:`version` 5).
It should be understood that these are supported only if the values contained
therein are themselves supported.
Recursive containers are supported since :data:`version` 3.
@@ -71,6 +72,10 @@ this module. The following types are supported:
Added format version 5, which allows marshalling slices.
+.. versionchanged:: next
+
+ Added format version 6, which allows marshalling :class:`frozendict`.
+
The module defines these functions:
@@ -173,6 +178,8 @@ In addition, the following constants are defined:
4 Python 3.4 Efficient representation of short strings
------- --------------- ----------------------------------------------------
5 Python 3.14 Support for :class:`slice` objects
+ ------- --------------- ----------------------------------------------------
+ 6 Python 3.15 Support for :class:`frozendict` objects
======= =============== ====================================================
diff --git a/Include/cpython/marshal.h b/Include/cpython/marshal.h
index 6c1f7f96b6a2e8..159459fcaec3d9 100644
--- a/Include/cpython/marshal.h
+++ b/Include/cpython/marshal.h
@@ -6,7 +6,7 @@ PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(const char *,
Py_ssize_t);
PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int);
-#define Py_MARSHAL_VERSION 5
+#define Py_MARSHAL_VERSION 6
PyAPI_FUNC(long) PyMarshal_ReadLongFromFile(FILE *);
PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *);
diff --git a/Lib/functools.py b/Lib/functools.py
index 9bc2ee7e8c894c..cd374631f16792 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -170,7 +170,7 @@ def _lt_from_ge(self, other):
return op_result
return not op_result
-_convert = {
+_convert = frozendict({
'__lt__': [('__gt__', _gt_from_lt),
('__le__', _le_from_lt),
('__ge__', _ge_from_lt)],
@@ -183,7 +183,7 @@ def _lt_from_ge(self, other):
'__ge__': [('__le__', _le_from_ge),
('__gt__', _gt_from_ge),
('__lt__', _lt_from_ge)]
-}
+})
def total_ordering(cls):
"""Class decorator that fills in missing ordering methods"""
diff --git a/Lib/gettext.py b/Lib/gettext.py
index 6c11ab2b1eb570..2f77f0e849e9ae 100644
--- a/Lib/gettext.py
+++ b/Lib/gettext.py
@@ -111,8 +111,9 @@ def _error(value):
('+', '-'),
('*', '/', '%'),
)
-_binary_ops = {op: i for i, ops in enumerate(_binary_ops, 1) for op in ops}
-_c2py_ops = {'||': 'or', '&&': 'and', '/': '//'}
+_binary_ops = frozendict({op: i for i, ops in enumerate(_binary_ops, 1)
+ for op in ops})
+_c2py_ops = frozendict({'||': 'or', '&&': 'and', '/': '//'})
def _parse(tokens, priority=-1):
diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py
index 92ad6352557640..4cd6f8367a1349 100644
--- a/Lib/json/decoder.py
+++ b/Lib/json/decoder.py
@@ -43,11 +43,11 @@ def __reduce__(self):
return self.__class__, (self.msg, self.doc, self.pos)
-_CONSTANTS = {
+_CONSTANTS = frozendict({
'-Infinity': NegInf,
'Infinity': PosInf,
'NaN': NaN,
-}
+})
HEXDIGITS = re.compile(r'[0-9A-Fa-f]{4}', FLAGS)
diff --git a/Lib/json/tool.py b/Lib/json/tool.py
index 050c2fe2161e3e..e0b944b197d38b 100644
--- a/Lib/json/tool.py
+++ b/Lib/json/tool.py
@@ -22,13 +22,13 @@
(?Pnull)
''', re.VERBOSE)
-_group_to_theme_color = {
+_group_to_theme_color = frozendict({
"key": "definition",
"string": "string",
"number": "number",
"boolean": "keyword",
"null": "keyword",
-}
+})
def _colorize_json(json_str, theme):
diff --git a/Lib/opcode.py b/Lib/opcode.py
index f016b8dc4a50b2..165f42baed94e3 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -46,81 +46,81 @@
hascompare = [opmap["COMPARE_OP"]]
-_cache_format = {
- "LOAD_GLOBAL": {
- "counter": 1,
- "index": 1,
- "module_keys_version": 1,
- "builtin_keys_version": 1,
- },
- "BINARY_OP": {
- "counter": 1,
- "descr": 4,
- },
- "UNPACK_SEQUENCE": {
- "counter": 1,
- },
- "COMPARE_OP": {
- "counter": 1,
- },
- "CONTAINS_OP": {
- "counter": 1,
- },
- "FOR_ITER": {
- "counter": 1,
- },
- "LOAD_SUPER_ATTR": {
- "counter": 1,
- },
- "LOAD_ATTR": {
- "counter": 1,
- "version": 2,
- "keys_version": 2,
- "descr": 4,
- },
- "STORE_ATTR": {
- "counter": 1,
- "version": 2,
- "index": 1,
- },
- "CALL": {
- "counter": 1,
- "func_version": 2,
- },
- "CALL_KW": {
- "counter": 1,
- "func_version": 2,
- },
- "CALL_FUNCTION_EX": {
- "counter": 1,
- },
- "STORE_SUBSCR": {
- "counter": 1,
- },
- "SEND": {
- "counter": 1,
- },
- "JUMP_BACKWARD": {
- "counter": 1,
- },
- "TO_BOOL": {
- "counter": 1,
- "version": 2,
- },
- "POP_JUMP_IF_TRUE": {
- "counter": 1,
- },
- "POP_JUMP_IF_FALSE": {
- "counter": 1,
- },
- "POP_JUMP_IF_NONE": {
- "counter": 1,
- },
- "POP_JUMP_IF_NOT_NONE": {
- "counter": 1,
- },
-}
+_cache_format = frozendict(
+ LOAD_GLOBAL=frozendict(
+ counter=1,
+ index=1,
+ module_keys_version=1,
+ builtin_keys_version=1,
+ ),
+ BINARY_OP=frozendict(
+ counter=1,
+ descr=4,
+ ),
+ UNPACK_SEQUENCE=frozendict(
+ counter=1,
+ ),
+ COMPARE_OP=frozendict(
+ counter=1,
+ ),
+ CONTAINS_OP=frozendict(
+ counter=1,
+ ),
+ FOR_ITER=frozendict(
+ counter=1,
+ ),
+ LOAD_SUPER_ATTR=frozendict(
+ counter=1,
+ ),
+ LOAD_ATTR=frozendict(
+ counter=1,
+ version=2,
+ keys_version=2,
+ descr=4,
+ ),
+ STORE_ATTR=frozendict(
+ counter=1,
+ version=2,
+ index=1,
+ ),
+ CALL=frozendict(
+ counter=1,
+ func_version=2,
+ ),
+ CALL_KW=frozendict(
+ counter=1,
+ func_version=2,
+ ),
+ CALL_FUNCTION_EX=frozendict(
+ counter=1,
+ ),
+ STORE_SUBSCR=frozendict(
+ counter=1,
+ ),
+ SEND=frozendict(
+ counter=1,
+ ),
+ JUMP_BACKWARD=frozendict(
+ counter=1,
+ ),
+ TO_BOOL=frozendict(
+ counter=1,
+ version=2,
+ ),
+ POP_JUMP_IF_TRUE=frozendict(
+ counter=1,
+ ),
+ POP_JUMP_IF_FALSE=frozendict(
+ counter=1,
+ ),
+ POP_JUMP_IF_NONE=frozendict(
+ counter=1,
+ ),
+ POP_JUMP_IF_NOT_NONE=frozendict(
+ counter=1,
+ ),
+)
-_inline_cache_entries = {
+_inline_cache_entries = frozendict({
name : sum(value.values()) for (name, value) in _cache_format.items()
-}
+})
diff --git a/Lib/optparse.py b/Lib/optparse.py
index 5ff7f74754f9c1..de1082442ef7f2 100644
--- a/Lib/optparse.py
+++ b/Lib/optparse.py
@@ -407,10 +407,12 @@ def _parse_num(val, type):
def _parse_int(val):
return _parse_num(val, int)
-_builtin_cvt = { "int" : (_parse_int, _("integer")),
- "long" : (_parse_int, _("integer")),
- "float" : (float, _("floating-point")),
- "complex" : (complex, _("complex")) }
+_builtin_cvt = frozendict({
+ "int": (_parse_int, _("integer")),
+ "long": (_parse_int, _("integer")),
+ "float": (float, _("floating-point")),
+ "complex": (complex, _("complex")),
+})
def check_builtin(option, opt, value):
(cvt, what) = _builtin_cvt[option.type]
diff --git a/Lib/platform.py b/Lib/platform.py
index 3a71b669985f13..9d7aa5c66a91cb 100644
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -127,7 +127,7 @@
# Based on the description of the PHP's version_compare():
# http://php.net/manual/en/function.version-compare.php
-_ver_stages = {
+_ver_stages = frozendict({
# any string not found in this dict, will get 0 assigned
'dev': 10,
'alpha': 20, 'a': 20,
@@ -136,7 +136,7 @@
'RC': 50, 'rc': 50,
# number, will get 100 assigned
'pl': 200, 'p': 200,
-}
+})
def _comparable_version(version):
@@ -705,11 +705,11 @@ def _syscmd_file(target, default=''):
# Default values for architecture; non-empty strings override the
# defaults given as parameters
-_default_architecture = {
+_default_architecture = frozendict({
'win32': ('', 'WindowsPE'),
'win16': ('', 'Windows'),
'dos': ('', 'MSDOS'),
-}
+})
def architecture(executable=sys.executable, bits='', linkage=''):
diff --git a/Lib/plistlib.py b/Lib/plistlib.py
index cae38672f641b7..3c6a6b7bdc44d2 100644
--- a/Lib/plistlib.py
+++ b/Lib/plistlib.py
@@ -453,7 +453,7 @@ class InvalidFileException (ValueError):
def __init__(self, message="Invalid file"):
ValueError.__init__(self, message)
-_BINARY_FORMAT = {1: 'B', 2: 'H', 4: 'L', 8: 'Q'}
+_BINARY_FORMAT = frozendict({1: 'B', 2: 'H', 4: 'L', 8: 'Q'})
_undefined = object()
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 612b32cd0765ec..896db17baeb3db 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -150,7 +150,8 @@
source=_ssl)
PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_TLS
-_PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()}
+_PROTOCOL_NAMES = frozendict({
+ value: name for name, value in _SSLMethod.__members__.items()})
_SSLv2_IF_EXISTS = getattr(_SSLMethod, 'PROTOCOL_SSLv2', None)
diff --git a/Lib/symtable.py b/Lib/symtable.py
index 45610fd5612995..c7152a70f5aa0b 100644
--- a/Lib/symtable.py
+++ b/Lib/symtable.py
@@ -414,7 +414,7 @@ def get_namespace(self):
_flags = [('USE', USE)]
_flags.extend(kv for kv in globals().items() if kv[0].startswith('DEF_'))
_scopes_names = ('FREE', 'LOCAL', 'GLOBAL_IMPLICIT', 'GLOBAL_EXPLICIT', 'CELL')
-_scopes_value_to_name = {globals()[n]: n for n in _scopes_names}
+_scopes_value_to_name = frozendict({globals()[n]: n for n in _scopes_names})
def main(args):
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 75984bf8b262b9..7abda3653e764b 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -859,11 +859,11 @@ def data_filter(member, dest_path):
return member.replace(**new_attrs, deep=False)
return member
-_NAMED_FILTERS = {
+_NAMED_FILTERS = frozendict({
"fully_trusted": fully_trusted_filter,
"tar": tar_filter,
"data": data_filter,
-}
+})
#------------------
# Exported Classes
diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py
index 28f24d0fc59cb0..78db4219e2997c 100644
--- a/Lib/test/test_marshal.py
+++ b/Lib/test/test_marshal.py
@@ -570,6 +570,15 @@ def testDict(self):
self.helper(dictobj)
self.helper3(dictobj)
+ def testFrozenDict(self):
+ for obj in self.keys:
+ dictobj = frozendict({"hello": obj, "goodbye": obj, obj: "hello"})
+ self.helper(dictobj)
+
+ for version in range(6):
+ with self.assertRaises(ValueError):
+ marshal.dumps(dictobj, version)
+
def testModule(self):
with open(__file__, "rb") as f:
code = f.read()
@@ -635,7 +644,7 @@ def test_slice(self):
with self.subTest(obj=str(obj)):
self.helper(obj)
- for version in range(4):
+ for version in range(5):
with self.assertRaises(ValueError):
marshal.dumps(obj, version)
diff --git a/Misc/NEWS.d/next/Library/2026-03-05-16-06-09.gh-issue-141510.dFPAQS.rst b/Misc/NEWS.d/next/Library/2026-03-05-16-06-09.gh-issue-141510.dFPAQS.rst
new file mode 100644
index 00000000000000..280a7b3632ddae
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-05-16-06-09.gh-issue-141510.dFPAQS.rst
@@ -0,0 +1,2 @@
+:mod:`marshal` now supports :class:`frozendict` objects. The marshal format
+version was increased to 6. Patch by Victor Stinner.
diff --git a/Programs/_freeze_module.c b/Programs/_freeze_module.c
index a5809b37b6b493..27a60171f3eca8 100644
--- a/Programs/_freeze_module.c
+++ b/Programs/_freeze_module.c
@@ -134,7 +134,7 @@ compile_and_marshal(const char *name, const char *text)
return NULL;
}
- assert(Py_MARSHAL_VERSION >= 5);
+ assert(Py_MARSHAL_VERSION >= 6);
PyObject *marshalled = PyMarshal_WriteObjectToString(code, Py_MARSHAL_VERSION);
Py_CLEAR(code);
if (marshalled == NULL) {
diff --git a/Python/marshal.c b/Python/marshal.c
index a71909f103ebfc..59db6456552c35 100644
--- a/Python/marshal.c
+++ b/Python/marshal.c
@@ -580,6 +580,12 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
Py_ssize_t pos;
PyObject *key, *value;
if (PyFrozenDict_CheckExact(v)) {
+ if (p->version < 6) {
+ w_byte(TYPE_UNKNOWN, p);
+ p->error = WFERR_UNMARSHALLABLE;
+ return;
+ }
+
W_TYPE(TYPE_FROZENDICT, p);
}
else {