Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

#!/usr/bin/env python 

 

""" 

 

Functools: Collections of useful functions/functors 

 

""" 

 

import hashlib 

import time 

import types 

import inspect 

from array import array 

from itertools import izip_longest 

from cStringIO import StringIO 

from . import utils 

 

#=============================================================================== 

 

def hashtimestamp(): 

""" 

Return a hash string based on curent timestamp. 

""" 

return hashlib.sha1(str(int(time.time()*1e6))).hexdigest() 

 

def prop(default, doc=""): 

""" 

To be used inside the class to provide complete set of get/set/del in one go. 

 

Usage: 

 

>>> class Foo: 

... xbin = prop(10 , "Number of bins on x-axis") 

>>> 

>>> foo = Foo() 

>>> foo.xbin 

10 

>>> foo.xbin = 20 

>>> foo.xbin 

20 

 

""" 

key = '__prop_'+hashtimestamp() 

getx = lambda self: getattr(self, key, default) 

setx = lambda self, val: setattr(self, key, val) 

delx = lambda self: delattr(self, key) 

return property(getx, setx, delx, doc) 

 

#=============================================================================== 

 

## Metaclass: Unsettable 

class Unsettable_type(type): 

def __setattr__(cls, attr, value): 

raise AttributeError("Setting attribute on this class is prohibited.") 

 

def staticprop(key0, doc): 

""" 

Like `prop` above, but allow the value to be set only once. 

The second set will raise AttributeError. 

Default value of every attribute is None. 

""" 

key = '_staticprop_'+key0 

getx = lambda self: getattr(self, key, None) 

delx = lambda self: delattr(self, key) 

def setx(self, value): 

""" 

Guard here against already-set value 

""" 

if hasattr(self, key): 

raise AttributeError('Attribute "%s" already set to %r' % (key0,getattr(self,key))) 

setattr(self, key, value) 

return property(getx, setx, delx, doc) 

 

## instance method to be bound 

def clear(self): # Same name as dict.clear 

""" 

Reset all its attribute values to default. 

""" 

for key in self.__slots__: 

if hasattr(self, key): 

delattr(self, key) 

 

#------------------------------------------------------------------------------- 

 

WIDTH_FULL = 80 

WIDTH_PANE = 18 

TEMPLATE_TOP = '{:-^%i}'%WIDTH_FULL 

TEMPLATE_HEAD = ' KEY | VALUE ( * differs from default )' 

TEMPLATE_ROW = '{key:%i}| {star} {strval}'%WIDTH_PANE 

 

## instance method to be bound 

def __str__(self): 

 

def msg(key, defval, strval): 

star = '*' if defval!=strval else ' ' 

if strval=='': 

strval = "''" 

key = key[:WIDTH_PANE] # Cutoff width 

return TEMPLATE_ROW.format(**locals()) 

 

def is_excluded(key): 

"""Exclude these fields.""" 

if key.startswith('_'): # Internal 

return True 

# if key in ('stats', 'wrapper', 'anchors', 'debug_messages', 'xarr', 'yarr', 'zarr'): # Proxy 

# return True 

return False 

 

def is_good_keyval(key, val): 

"""Return True if this key-val should be listed.""" 

if inspect.ismethod(val): # method 

return False 

if isinstance(val, types.BuiltinFunctionType): 

return False 

return True 

 

cls = self.__class__ 

default_instance = cls() 

fullmsg = StringIO() 

addressmsg = '%s.%s @ %s'%(cls.__module__, cls.__name__, hex(id(self))) 

print >>fullmsg, TEMPLATE_TOP.format(addressmsg) 

print >>fullmsg, TEMPLATE_HEAD 

for key in sorted(dir(self)): 

## skip some entries by its key/val 

if is_excluded(key): 

continue 

 

## Obtain the value, skip unnecessary ones. 

# val = getattr(self, key, None) 

try: 

val = getattr(self, key, None) 

except: 

val = 'EXCEPTION' # some attribute may not be ready yet 

if not is_good_keyval(key, val): 

continue 

strval = str(val) 

 

## Start printing, prep default value & current value 

# defval = str(getattr(default_instance, key, None)) 

try: 

defval = str(getattr(default_instance, key, None)) 

except: 

defval = 'EXCEPTION' 

 

## Pretty array 

if isinstance(val, array): 

print >>fullmsg, msg(key, defval, utils.pretty_array(val)) 

continue 

 

## Pretty paragraph string 

if '\n' in strval: 

defval = defval.strip().split('\n') 

strval = strval.strip().split('\n') 

for i, (dval, sval) in enumerate(izip_longest(defval, strval)): 

line = msg(key if i==0 else '', dval, sval) 

print >> fullmsg, line 

continue 

 

## Break into several lines instead if it's a long list 

if len(strval)>60 and isinstance(val, (list, tuple)): 

for i,subval in enumerate(val): 

if i==0: 

line = msg(key, defval, str(subval)) 

else: # Omit the key+defval 

line = msg('', None, str(subval)) 

print >>fullmsg, line 

continue 

 

## Default printing 

print >>fullmsg, msg(key, defval, strval) 

 

## Finally, line break & return 

print >>fullmsg, '-'*WIDTH_FULL 

return fullmsg.getvalue() 

 

#------------------------------------------------------------------------------- 

 

def static_struct(typename, **kwargs): 

""" 

Helper method to generate static class (inspired by collections.namedtuple), 

to be used exclusively in conjunction with staticprop. 

 

By static, it means: 

 

- All attribute is declared when class is created. 

- No attribute can be added furthermore, either at class-level or instance-level. 

- Attribute, once assigned, cannot be changed. 

- Unassigned attribute has 'None' as default value. 

 

Need this wrapper in order to properly generate ``__slots__`` with keys 

populated from staticprop 

 

Usage:: 

 

>>> ## Define the class first 

>>> Cls = static_struct('MyClass', attr1='doc', attr2='doc2') 

>>> Cls 

<class 'qhist.functools.MyClass'> 

 

## Dynamic properties are populated and inspectable. 

>>> [ x for x in dir(Cls) if not x.startswith('_') ] 

['attr1', 'attr2', 'clear', 'keys', 'reset'] 

 

## Create class instance 

>>> obj = Cls() 

 

## Default value is None 

>>> obj.attr1 == None 

True 

 

## Set the value. Set again will fail 

>>> obj.attr1 = 42 # set the value 

>>> obj.attr1 

42 

>>> obj.attr1 = 555 

Traceback (most recent call last): 

... 

AttributeError: Attribute "attr1" already set to 42 

 

## Setting 'None' respect the no-repeat rule above 

>>> obj_none = Cls() 

>>> obj_none.attr1 = None 

>>> obj_none.attr1 = None # again 

Traceback (most recent call last): 

... 

AttributeError: Attribute "attr1" already set to None 

 

## Get/Set will fail if value is not defined. 

>>> obj.unknown 

Traceback (most recent call last): 

... 

AttributeError: 'MyClass' object has no attribute 'unknown' 

>>> obj.unknown = 42 

Traceback (most recent call last): 

... 

AttributeError: 'MyClass' object has no attribute 'unknown' 

 

## Cannot set on the class directly, for value safety 

>>> Cls.attr1 = 'some value' 

Traceback (most recent call last): 

... 

AttributeError: ... 

>>> Cls.attr1 

<property object at ...> 

 

## Check independence between instance. 

>>> obj2 = Cls() 

>>> obj.attr2 = 111 

>>> obj2.attr2 = 222 

>>> obj.attr2 

111 

>>> obj2.attr2 

222 

 

## This is like a dictionary 

>>> obj.keys() 

['attr1', 'attr2'] 

 

## Use clear/reset to clear the values back to unset 

# This is independent for different instance 

>>> obj.clear() 

>>> obj.attr1 == obj.attr2 == None 

True 

>>> obj2.attr2 

222 

 

## Attributes can be pretty-print as table 

>>> print(str(obj2)) 

-------------------... 

KEY | VALUE ( * differs from default ) 

attr1 | None 

attr2 | * 222 

-------------------------------------------------------... 

 

""" 

## Instantiate the props first 

props = { key:staticprop(key,val) for key,val in kwargs.iteritems() } 

 

## Fixed the attributes of the class. 

# Setter-on-class is disable from this point. 

attrs = dict(props) 

attrs['clear'] = clear 

attrs['reset'] = clear # alias 

attrs['keys'] = lambda _: sorted(kwargs.keys()) # like a dictionary 

attrs['__slots__'] = tuple(['_staticprop_'+key for key in props]) # same as above 

attrs['__str__'] = __str__ 

Cls = Unsettable_type(typename, (), attrs) 

return Cls 

 

#===============================================================================