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

54 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:51 +0000

1# -*- coding: utf-8 -*- 

2import warnings 

3import json 

4 

5from tarfile import TarFile 

6from pkgutil import get_data 

7from io import BytesIO 

8 

9from dateutil.tz import tzfile as _tzfile 

10 

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

12 

13ZONEFILENAME = "dateutil-zoneinfo.tar.gz" 

14METADATA_FN = 'METADATA' 

15 

16 

17class tzfile(_tzfile): 

18 def __reduce__(self): 

19 return (gettz, (self._filename,)) 

20 

21 

22def getzoneinfofile_stream(): 

23 try: 

24 return BytesIO(get_data(__name__, ZONEFILENAME)) 

25 except IOError as e: # TODO switch to FileNotFoundError? 

26 warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) 

27 return None 

28 

29 

30class ZoneInfoFile(object): 

31 def __init__(self, zonefile_stream=None): 

32 if zonefile_stream is not None: 

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

34 self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name) 

35 for zf in tf.getmembers() 

36 if zf.isfile() and zf.name != METADATA_FN} 

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

38 # waste of memory 

39 links = {zl.name: self.zones[zl.linkname] 

40 for zl in tf.getmembers() if 

41 zl.islnk() or zl.issym()} 

42 self.zones.update(links) 

43 try: 

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

45 metadata_str = metadata_json.read().decode('UTF-8') 

46 self.metadata = json.loads(metadata_str) 

47 except KeyError: 

48 # no metadata in tar file 

49 self.metadata = None 

50 else: 

51 self.zones = {} 

52 self.metadata = None 

53 

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

55 """ 

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

57 for retrieving zones from the zone dictionary. 

58 

59 :param name: 

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

61 

62 :param default: 

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

64 

65 .. versionadded:: 2.6.0 

66 

67 """ 

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

69 

70 

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

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

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

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

75# 

76# TODO: Remove after deprecation period. 

77_CLASS_ZONE_INSTANCE = [] 

78 

79 

80def get_zonefile_instance(new_instance=False): 

81 """ 

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

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

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

85 

86 :param new_instance: 

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

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

89 are created only as necessary. 

90 

91 :return: 

92 Returns a :class:`ZoneInfoFile` object. 

93 

94 .. versionadded:: 2.6 

95 """ 

96 if new_instance: 

97 zif = None 

98 else: 

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

100 

101 if zif is None: 

102 zif = ZoneInfoFile(getzoneinfofile_stream()) 

103 

104 get_zonefile_instance._cached_instance = zif 

105 

106 return zif 

107 

108 

109def gettz(name): 

110 """ 

111 This retrieves a time zone from the local zoneinfo tarball that is packaged 

112 with dateutil. 

113 

114 :param name: 

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

116 

117 :return: 

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

119 

120 .. warning:: 

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

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

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

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

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

126 date compared to the system zoneinfo). 

127 

128 .. deprecated:: 2.6 

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

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

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

132 

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

134 dateutil-provided zoneinfo. 

135 """ 

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

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

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

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

140 DeprecationWarning) 

141 

142 if len(_CLASS_ZONE_INSTANCE) == 0: 

143 _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) 

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

145 

146 

147def gettz_db_metadata(): 

148 """ Get the zonefile metadata 

149 

150 See `zonefile_metadata`_ 

151 

152 :returns: 

153 A dictionary with the database metadata 

154 

155 .. deprecated:: 2.6 

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

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

158 """ 

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

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

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

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

163 DeprecationWarning) 

164 

165 if len(_CLASS_ZONE_INSTANCE) == 0: 

166 _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) 

167 return _CLASS_ZONE_INSTANCE[0].metadata