Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/dateutil/zoneinfo/__init__.py: 7%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

72 statements  

1""" 

2The ``dateutil.zoneinfo`` module was originally designed to work with a tarball 

3shipped with ``dateutil``. Since ``dateutil`` now uses data from the same 

4sources as :py:mod:`zoneinfo`, this module is no longer necessary. 

5 

6.. caution:: 

7 This module is deprecated, and its functionality has been dramatically 

8 curtailed. It is recommended that you adopt the standard library module 

9 :py:mod:`zoneinfo` or :func:`dateutil.tz.gettz` instead. 

10 

11""" 

12 

13import json 

14import sys 

15import warnings 

16from tarfile import TarFile 

17 

18 

19from dateutil import _tzdata_impl 

20from dateutil.tz import tzfile as _tzfile 

21 

22__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] 

23 

24ZONEFILENAME = None 

25METADATA_FN = 'METADATA' 

26 

27 

28def _importer_stacklevel(): 

29 """The ``stacklevel`` that attributes a warning to the importing code. 

30 

31 This module can be imported through ``dateutil.__getattr__`` (e.g. by 

32 ``from dateutil import zoneinfo``), which calls 

33 ``importlib.import_module``; the warnings machinery skips the import 

34 system's own frames, but not those two, so skip them here. 

35 """ 

36 level = 1 

37 frame = sys._getframe(1) # The body of this module 

38 while frame.f_back is not None: 

39 frame = frame.f_back 

40 filename = frame.f_code.co_filename 

41 if "importlib" in filename and "_bootstrap" in filename: 

42 # The warnings machinery skips these frames without counting them. 

43 continue 

44 

45 level += 1 

46 if frame.f_globals.get("__name__") not in ("importlib", "dateutil"): 

47 return level 

48 

49 return 2 

50 

51 

52warnings.warn( 

53 "The `dateutil.zoneinfo` module has been replaced with a wrapper around " 

54 "the tzdata package, and its use is deprecated, to be removed in a future " 

55 "version. Use the standard library module `zoneinfo` or `dateutil.tz` " 

56 "instead.", 

57 DeprecationWarning, 

58 stacklevel=_importer_stacklevel(), 

59) 

60 

61 

62tzfile = _tzfile 

63 

64class ZoneInfoFile: 

65 def __init__(self, zonefile_stream=None): 

66 if zonefile_stream is not None: 

67 self._load_legacy_zonefile_stream(zonefile_stream) 

68 else: 

69 self._eager_load_tzdata() 

70 

71 def _eager_load_tzdata(self): 

72 try: 

73 import tzdata 

74 except ImportError as e: 

75 raise ImportError( 

76 "The tzdata module is required to use ZoneInfoFile; either add a dependency on tzdata or migrate away from dateutil.zoneinfo." 

77 ) from e 

78 

79 zone_dict = {} 

80 

81 for key in _tzdata_impl._load_tzdata_keys(): 

82 with _tzdata_impl._load_tzdata(key) as f: 

83 zone_dict[key] = tzfile(f, key=key) 

84 

85 self.zones = zone_dict 

86 self.metadata = { 

87 "metadata_version": "3.0", 

88 "tzversion": tzdata.IANA_VERSION, 

89 } 

90 

91 def _load_legacy_zonefile_stream(self, zonefile_stream): 

92 with TarFile.open(fileobj=zonefile_stream) as tf: 

93 self.zones = { 

94 zf.name: tzfile(tf.extractfile(zf), filename=zf.name) 

95 for zf in tf.getmembers() 

96 if zf.isfile() and zf.name != METADATA_FN 

97 } 

98 # deal with links: They'll point to their parent object. Less 

99 # waste of memory 

100 links = { 

101 zl.name: self.zones[zl.linkname] 

102 for zl in tf.getmembers() 

103 if zl.islnk() or zl.issym() 

104 } 

105 self.zones.update(links) 

106 try: 

107 metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) 

108 metadata_str = metadata_json.read().decode("UTF-8") 

109 self.metadata = json.loads(metadata_str) 

110 except KeyError: 

111 # no metadata in tar file 

112 self.metadata = None 

113 

114 def get(self, name, default=None): 

115 """ 

116 Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method 

117 for retrieving zones from the zone dictionary. 

118 

119 :param name: 

120 The name of the zone to retrieve. (Generally IANA zone names) 

121 

122 :param default: 

123 The value to return in the event of a missing key. 

124 

125 .. versionadded:: 2.6.0 

126 

127 """ 

128 return self.zones.get(name, default) 

129 

130 

131# The current API has gettz as a module function, although in fact it taps into 

132# a stateful class. So as a workaround for now, without changing the API, we 

133# will create a new "global" class instance the first time a user requests a 

134# timezone. Ugly, but adheres to the api. 

135# 

136# TODO: Remove after deprecation period. 

137_CLASS_ZONE_INSTANCE = [] 

138 

139 

140def get_zonefile_instance(new_instance=False): 

141 """ 

142 This is a convenience function which provides a :class:`ZoneInfoFile` 

143 instance using the data provided by the ``dateutil`` package. By default, it 

144 caches a single instance of the ZoneInfoFile object and returns that. 

145 

146 :param new_instance: 

147 If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and 

148 used as the cached instance for the next call. Otherwise, new instances 

149 are created only as necessary. 

150 

151 :return: 

152 Returns a :class:`ZoneInfoFile` object. 

153 

154 .. versionadded:: 2.6 

155 """ 

156 if new_instance: 

157 zif = None 

158 else: 

159 zif = getattr(get_zonefile_instance, '_cached_instance', None) 

160 

161 if zif is None: 

162 zif = ZoneInfoFile() 

163 

164 get_zonefile_instance._cached_instance = zif 

165 

166 return zif 

167 

168 

169def gettz(name): 

170 """ 

171 This retrieves a time zone from the ``tzdata`` package. 

172 

173 :param name: 

174 An IANA-style time zone name, as found in the zoneinfo file. 

175 

176 :return: 

177 Returns a :class:`dateutil.tz.tzfile` time zone object. 

178 

179 .. warning:: 

180 It is generally inadvisable to use this function, and it is only 

181 provided for API compatibility with earlier versions. This is *not* 

182 equivalent to ``dateutil.tz.gettz()``, which selects an appropriate 

183 time zone based on the inputs, favoring system zoneinfo. This is ONLY 

184 for accessing the dateutil-specific zoneinfo (which may be out of 

185 date compared to the system zoneinfo). 

186 

187 .. deprecated:: 2.6 

188 If you need to use a specific zoneinfofile over the system zoneinfo, 

189 instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call 

190 :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. 

191 

192 Use :func:`get_zonefile_instance` to retrieve an instance of the 

193 dateutil-provided zoneinfo. 

194 """ 

195 warnings.warn("zoneinfo.gettz() will be removed in future versions, " 

196 "to use the dateutil-provided zoneinfo files, instantiate a " 

197 "ZoneInfoFile object and use ZoneInfoFile.zones.get() " 

198 "instead. See the documentation for details.", 

199 DeprecationWarning) 

200 

201 if len(_CLASS_ZONE_INSTANCE) == 0: 

202 _CLASS_ZONE_INSTANCE.append(ZoneInfoFile()) 

203 return _CLASS_ZONE_INSTANCE[0].zones.get(name) 

204 

205 

206def gettz_db_metadata(): 

207 """Get the zonefile metadata 

208 

209 :returns: 

210 A dictionary with the database metadata 

211 

212 .. deprecated:: 2.6 

213 See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, 

214 query the attribute ``zoneinfo.ZoneInfoFile.metadata``. 

215 """ 

216 warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " 

217 "versions, to use the dateutil-provided zoneinfo files, " 

218 "ZoneInfoFile object and query the 'metadata' attribute " 

219 "instead. See the documentation for details.", 

220 DeprecationWarning) 

221 

222 if len(_CLASS_ZONE_INSTANCE) == 0: 

223 _CLASS_ZONE_INSTANCE.append(ZoneInfoFile()) 

224 return _CLASS_ZONE_INSTANCE[0].metadata