Coverage for aiocoap/util/__init__.py: 90%

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

51 statements  

1# This file is part of the Python aiocoap library project. 

2# 

3# Copyright (c) 2012-2014 Maciej Wasilak <http://sixpinetrees.blogspot.com/>, 

4# 2013-2014 Christian Amsüss <c.amsuess@energyharvesting.at> 

5# 

6# aiocoap is free software, this file is published under the MIT license as 

7# described in the accompanying LICENSE file. 

8 

9"""Tools not directly related with CoAP that are needed to provide the API 

10 

11These are only part of the stable API to the extent they are used by other APIs 

12-- for example, you can use the type constructor of :class:`ExtensibleEnumMeta` 

13when creating an :class:`aiocoap.numbers.optionnumbers.OptionNumber`, but don't 

14expect it to be usable in a stable way for own extensions. 

15 

16Most functions are available in submodules; some of them may only have 

17components that are exclusively used internally and never part of the public 

18API even in the limited fashion stated above. 

19 

20.. toctree:: 

21 :glob: 

22 

23 aiocoap.util.* 

24""" 

25 

26import urllib.parse 

27 

28class ExtensibleEnumMeta(type): 

29 """Metaclass for ExtensibleIntEnum, see there for detailed explanations""" 

30 def __init__(self, name, bases, dict): 

31 self._value2member_map_ = {} 

32 for k, v in dict.items(): 

33 if k.startswith('_'): 

34 continue 

35 if callable(v): 

36 continue 

37 if isinstance(v, property): 

38 continue 

39 if isinstance(v, classmethod): 

40 continue 

41 instance = self(v) 

42 instance.name = k 

43 setattr(self, k, instance) 

44 type.__init__(self, name, bases, dict) 

45 

46 def __call__(self, value): 

47 if isinstance(value, self): 

48 return value 

49 if value not in self._value2member_map_: 

50 self._value2member_map_[value] = super(ExtensibleEnumMeta, self).__call__(value) 

51 return self._value2member_map_[value] 

52 

53class ExtensibleIntEnum(int, metaclass=ExtensibleEnumMeta): 

54 """Similar to Python's enum.IntEnum, this type can be used for named 

55 numbers which are not comprehensively known, like CoAP option numbers.""" 

56 

57 def __repr__(self): 

58 return '<%s %d%s>'%(type(self).__name__, self, ' "%s"'%self.name if hasattr(self, "name") else "") 

59 

60 def __str__(self): 

61 return self.name if hasattr(self, "name") else int.__str__(self) 

62 

63def hostportjoin(host, port=None): 

64 """Join a host and optionally port into a hostinfo-style host:port 

65 string 

66 

67 >>> hostportjoin('example.com') 

68 'example.com' 

69 >>> hostportjoin('example.com', 1234) 

70 'example.com:1234' 

71 >>> hostportjoin('127.0.0.1', 1234) 

72 '127.0.0.1:1234' 

73 

74 This is lax with respect to whether host is an IPv6 literal in brackets or 

75 not, and accepts either form; IP-future literals that do not contain a 

76 colon must be already presented in their bracketed form: 

77 

78 >>> hostportjoin('2001:db8::1') 

79 '[2001:db8::1]' 

80 >>> hostportjoin('2001:db8::1', 1234) 

81 '[2001:db8::1]:1234' 

82 >>> hostportjoin('[2001:db8::1]', 1234) 

83 '[2001:db8::1]:1234' 

84 """ 

85 if ':' in host and not (host.startswith('[') and host.endswith(']')): 

86 host = '[%s]'%host 

87 

88 if port is None: 

89 hostinfo = host 

90 else: 

91 hostinfo = "%s:%d"%(host, port) 

92 return hostinfo 

93 

94def hostportsplit(hostport): 

95 """Like urllib.parse.splitport, but return port as int, and as None if not 

96 given. Also, it allows giving IPv6 addresses like a netloc: 

97 

98 >>> hostportsplit('foo') 

99 ('foo', None) 

100 >>> hostportsplit('foo:5683') 

101 ('foo', 5683) 

102 >>> hostportsplit('[::1%eth0]:56830') 

103 ('::1%eth0', 56830) 

104 """ 

105 

106 pseudoparsed = urllib.parse.SplitResult(None, hostport, None, None, None) 

107 try: 

108 return pseudoparsed.hostname, pseudoparsed.port 

109 except ValueError: 

110 if '[' not in hostport and hostport.count(':') > 1: 

111 raise ValueError("Could not parse network location. " 

112 "Beware that when IPv6 literals are expressed in URIs, they " 

113 "need to be put in square brackets to distinguish them from " 

114 "port numbers.") 

115 raise 

116 

117def quote_nonascii(s): 

118 """Like urllib.parse.quote, but explicitly only escaping non-ascii characters. 

119 

120 This function is deprecated due to it use of the irrelevant "being an ASCII 

121 character" property (when instead RFC3986 productions like "unreserved" 

122 should be used), and due for removal when aiocoap's URI processing is 

123 overhauled the next time. 

124 """ 

125 

126 return "".join(chr(c) if c <= 127 else "%%%02X" % c for c in s.encode('utf8')) 

127 

128class Sentinel: 

129 """Class for sentinel that can only be compared for identity. No efforts 

130 are taken to make these singletons; it is up to the users to always refer 

131 to the same instance, which is typically defined on module level.""" 

132 def __init__(self, label): 

133 self._label = label 

134 

135 def __repr__(self): 

136 return '<%s>' % self._label