Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tables/path.py: 43%

58 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-10 06:15 +0000

1"""Functionality related with node paths in a PyTables file. 

2 

3Variables 

4========= 

5 

6`__docformat`__ 

7 The format of documentation strings in this module. 

8 

9""" 

10 

11import re 

12import warnings 

13import keyword 

14 

15from .exceptions import NaturalNameWarning 

16 

17__docformat__ = 'reStructuredText' 

18"""The format of documentation strings in this module.""" 

19 

20 

21_python_id_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') 

22"""Python identifier regular expression.""" 

23 

24_reserved_id_re = re.compile('^_[cfgv]_') 

25"""PyTables reserved identifier regular expression. 

26 

27- c: class variables 

28- f: class public methods 

29- g: class private methods 

30- v: instance variables 

31""" 

32 

33_hidden_name_re = re.compile('^_[pi]_') 

34"""Nodes with a name *matching* this expression are considered hidden. 

35 

36For instance, ``name`` whould be visible while ``_i_name`` would not. 

37""" 

38 

39_hidden_path_re = re.compile('/_[pi]_') 

40"""Nodes with a path *containing* this expression are considered hidden. 

41 

42For instance, a node with a pathname like ``/a/b/c`` would be visible 

43while nodes with pathnames like ``/a/c/_i_x`` or ``/a/_p_x/y`` would 

44not. 

45""" 

46 

47_warnInfo = ( 

48 "you will not be able to use natural naming to access this object; " 

49 "using ``getattr()`` will still work, though") 

50"""Warning printed when a name will not be reachable through natural naming""" 

51 

52 

53def check_attribute_name(name): 

54 """Check the validity of the `name` of an attribute in AttributeSet. 

55 

56 If the name is not valid, a ``ValueError`` is raised. If it is 

57 valid but it can not be used with natural naming, a 

58 `NaturalNameWarning` is issued. 

59 

60 >>> warnings.simplefilter("ignore") 

61 >>> check_attribute_name('a') 

62 >>> check_attribute_name('a_b') 

63 >>> check_attribute_name('a:b') # NaturalNameWarning 

64 >>> check_attribute_name('/a/b') # NaturalNameWarning 

65 >>> check_attribute_name('/') # NaturalNameWarning 

66 >>> check_attribute_name('.') # NaturalNameWarning 

67 >>> check_attribute_name('__members__') 

68 Traceback (most recent call last): 

69 ... 

70 ValueError: ``__members__`` is not allowed as an object name 

71 >>> check_attribute_name(1) 

72 Traceback (most recent call last): 

73 ... 

74 TypeError: object name is not a string: 1 

75 >>> check_attribute_name('') 

76 Traceback (most recent call last): 

77 ... 

78 ValueError: the empty string is not allowed as an object name 

79 """ 

80 if not isinstance(name, str): # Python >= 2.3 

81 raise TypeError(f"object name is not a string: {name!r}") 

82 

83 if name == '': 

84 raise ValueError("the empty string is not allowed as an object name") 

85 

86 # Check whether `name` is a valid Python identifier. 

87 if not _python_id_re.match(name): 

88 warnings.warn("object name is not a valid Python identifier: %r; " 

89 "it does not match the pattern ``%s``; %s" 

90 % (name, _python_id_re.pattern, _warnInfo), 

91 NaturalNameWarning, stacklevel=2) 

92 return 

93 

94 # However, Python identifiers and keywords have the same form. 

95 if keyword.iskeyword(name): 

96 warnings.warn("object name is a Python keyword: %r; %s" 

97 % (name, _warnInfo), NaturalNameWarning, stacklevel=2) 

98 return 

99 

100 # Still, names starting with reserved prefixes are not allowed. 

101 if _reserved_id_re.match(name): 

102 raise ValueError("object name starts with a reserved prefix: %r; " 

103 "it matches the pattern ``%s``" 

104 % (name, _reserved_id_re.pattern)) 

105 

106 # ``__members__`` is the only exception to that rule. 

107 if name == '__members__': 

108 raise ValueError("``__members__`` is not allowed as an object name") 

109 

110 

111def check_name_validity(name): 

112 """Check the validity of the `name` of a Node object, which more limited 

113 than attribute names. 

114 

115 If the name is not valid, a ``ValueError`` is raised. If it is 

116 valid but it can not be used with natural naming, a 

117 `NaturalNameWarning` is issued. 

118 

119 >>> warnings.simplefilter("ignore") 

120 >>> check_name_validity('a') 

121 >>> check_name_validity('a_b') 

122 >>> check_name_validity('a:b') # NaturalNameWarning 

123 >>> check_name_validity('/a/b') 

124 Traceback (most recent call last): 

125 ... 

126 ValueError: the ``/`` character is not allowed in object names: '/a/b' 

127 >>> check_name_validity('.') 

128 Traceback (most recent call last): 

129 ... 

130 ValueError: ``.`` is not allowed as an object name 

131 >>> check_name_validity('') 

132 Traceback (most recent call last): 

133 ... 

134 ValueError: the empty string is not allowed as an object name 

135 

136 """ 

137 check_attribute_name(name) 

138 

139 # Check whether `name` is a valid HDF5 name. 

140 # http://hdfgroup.org/HDF5/doc/UG/03_Model.html#Structure 

141 if name == '.': 

142 raise ValueError("``.`` is not allowed as an object name") 

143 elif '/' in name: 

144 raise ValueError("the ``/`` character is not allowed " 

145 "in object names: %r" % name) 

146 

147 

148def join_path(parentpath, name): 

149 """Join a *canonical* `parentpath` with a *non-empty* `name`. 

150 

151 .. versionchanged:: 3.0 

152 The *parentPath* parameter has been renamed into *parentpath*. 

153 

154 >>> join_path('/', 'foo') 

155 '/foo' 

156 >>> join_path('/foo', 'bar') 

157 '/foo/bar' 

158 >>> join_path('/foo', '/foo2/bar') 

159 '/foo/foo2/bar' 

160 >>> join_path('/foo', '/') 

161 '/foo' 

162 

163 """ 

164 

165 if name.startswith('./'): # Support relative paths (mainly for links) 

166 name = name[2:] 

167 if parentpath == '/' and name.startswith('/'): 

168 pstr = '%s' % name 

169 elif parentpath == '/' or name.startswith('/'): 

170 pstr = f'{parentpath}{name}' 

171 else: 

172 pstr = f'{parentpath}/{name}' 

173 if pstr.endswith('/'): 

174 pstr = pstr[:-1] 

175 return pstr 

176 

177 

178def split_path(path): 

179 """Split a *canonical* `path` into a parent path and a node name. 

180 

181 The result is returned as a tuple. The parent path does not 

182 include a trailing slash. 

183 

184 >>> split_path('/') 

185 ('/', '') 

186 >>> split_path('/foo/bar') 

187 ('/foo', 'bar') 

188 

189 """ 

190 

191 lastslash = path.rfind('/') 

192 ppath = path[:lastslash] 

193 name = path[lastslash + 1:] 

194 

195 if ppath == '': 

196 ppath = '/' 

197 

198 return (ppath, name) 

199 

200 

201def isvisiblename(name): 

202 """Does this `name` make the named node a visible one?""" 

203 

204 return _hidden_name_re.match(name) is None 

205 

206 

207def isvisiblepath(path): 

208 """Does this `path` make the named node a visible one?""" 

209 

210 return _hidden_path_re.search(path) is None 

211 

212 

213def _test(): 

214 """Run ``doctest`` on this module.""" 

215 

216 import doctest 

217 doctest.testmod() 

218 

219 

220if __name__ == '__main__': 

221 _test()