31
31
)
32
32
33
33
import sys
34
- import types
35
34
36
- __all__ = []
37
-
38
-
39
- # all() and any() recipes
40
- if sys .hexversion < 0x02050000 :
41
- __all__ .append ('all' )
42
- def all (iterable ):
43
- for element in iterable :
44
- if not element :
45
- return False
46
- return True
47
-
48
- __all__ .append ('any' )
49
- def any (iterable ):
50
- for element in iterable :
51
- if element :
52
- return True
53
- return False
54
-
55
-
56
- __all__ .append ('attrgetter' )
57
- __all__ .append ('itemgetter' )
58
- # multiple item extraction in itemgetter and attrgetter
59
- if sys .hexversion < 0x02050000 :
60
- def attrgetter (* attrs ):
61
- if len (attrs ) == 0 :
62
- raise TypeError ('attrgetter expected 1 arguments, got 0' )
63
- elif len (attrs ) == 1 :
64
- attr = attrs [0 ]
65
- def getter (obj ):
66
- return getattr (obj , attr )
67
- else :
68
- def getter (obj ):
69
- return tuple (getattr (obj , attr ) for attr in attrs )
70
- return getter
71
-
72
- def itemgetter (* items ):
73
- if len (items ) == 0 :
74
- raise TypeError ('itemgetter expected 1 arguments, got 0' )
75
- elif len (items ) == 1 :
76
- item = items [0 ]
77
- def getter (obj ):
78
- return obj [item ]
79
- else :
80
- def getter (obj ):
81
- return tuple (obj [item ] for item in items )
82
- return getter
83
- else :
84
- from operator import itemgetter , attrgetter
85
-
86
-
87
- # Named tuple recipe (http://code.activestate.com/recipes/500261/)
88
- __all__ .append ('namedtuple' )
89
- if sys .hexversion < 0x02060000 :
90
- from operator import itemgetter as _itemgetter
91
- from keyword import iskeyword as _iskeyword
92
- import sys as _sys
93
-
94
- def namedtuple (typename , field_names , verbose = False ):
95
- """Returns a new subclass of tuple with named fields.
96
-
97
- >>> Point = namedtuple('Point', 'x y')
98
- >>> Point.__doc__ # docstring for the new class
99
- 'Point(x, y)'
100
- >>> p = Point(11, y=22) # instantiate with positional args or keywords
101
- >>> p[0] + p[1] # indexable like a plain tuple
102
- 33
103
- >>> x, y = p # unpack like a regular tuple
104
- >>> x, y
105
- (11, 22)
106
- >>> p.x + p.y # fields also accessable by name
107
- 33
108
- >>> d = p._asdict() # convert to a dictionary
109
- >>> d['x']
110
- 11
111
- >>> Point(**d) # convert from a dictionary
112
- Point(x=11, y=22)
113
- >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
114
- Point(x=100, y=22)
115
-
116
- """
117
-
118
- # Parse and validate the field names. Validation serves two purposes,
119
- # generating informative error messages and preventing template injection attacks.
120
- if isinstance (field_names , basestring ):
121
- field_names = field_names .replace (',' , ' ' ).split () # names separated by whitespace and/or commas
122
- field_names = tuple (map (str , field_names ))
123
- for name in (typename ,) + field_names :
124
- if not min (c .isalnum () or c == '_' for c in name ):
125
- raise ValueError ('Type names and field names can only contain alphanumeric characters and underscores: %r' % name )
126
- if _iskeyword (name ):
127
- raise ValueError ('Type names and field names cannot be a keyword: %r' % name )
128
- if name [0 ].isdigit ():
129
- raise ValueError ('Type names and field names cannot start with a number: %r' % name )
130
- seen_names = set ()
131
- for name in field_names :
132
- if name .startswith ('_' ):
133
- raise ValueError ('Field names cannot start with an underscore: %r' % name )
134
- if name in seen_names :
135
- raise ValueError ('Encountered duplicate field name: %r' % name )
136
- seen_names .add (name )
137
-
138
- # Create and fill-in the class template
139
- numfields = len (field_names )
140
- argtxt = repr (field_names ).replace ("'" , "" )[1 :- 1 ] # tuple repr without parens or quotes
141
- reprtxt = ', ' .join ('%s=%%r' % name for name in field_names )
142
- dicttxt = ', ' .join ('%r: t[%d]' % (name , pos ) for pos , name in enumerate (field_names ))
143
- template = '''\
144
- class %(typename)s(tuple):
145
- '%(typename)s(%(argtxt)s)' \n
146
- __slots__ = () \n
147
- _fields = %(field_names)r \n
148
- def __new__(cls, %(argtxt)s):
149
- return tuple.__new__(cls, (%(argtxt)s)) \n
150
- @classmethod
151
- def _make(cls, iterable, new=tuple.__new__, len=len):
152
- 'Make a new %(typename)s object from a sequence or iterable'
153
- result = new(cls, iterable)
154
- if len(result) != %(numfields)d:
155
- raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
156
- return result \n
157
- def __repr__(self):
158
- return '%(typename)s(%(reprtxt)s)' %% self \n
159
- def _asdict(t):
160
- 'Return a new dict which maps field names to their values'
161
- return {%(dicttxt)s} \n
162
- def _replace(self, **kwds):
163
- 'Return a new %(typename)s object replacing specified fields with new values'
164
- result = self._make(map(kwds.pop, %(field_names)r, self))
165
- if kwds:
166
- raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
167
- return result \n \n ''' % locals ()
168
- for i , name in enumerate (field_names ):
169
- template += ' %s = property(itemgetter(%d))\n ' % (name , i )
170
- if verbose :
171
- print template
172
-
173
- # Execute the template string in a temporary namespace
174
- namespace = dict (itemgetter = _itemgetter )
175
- try :
176
- exec template in namespace
177
- except SyntaxError , e :
178
- raise SyntaxError (e .message + ':\n ' + template )
179
- result = namespace [typename ]
180
-
181
- # For pickling to work, the __module__ variable needs to be set to the frame
182
- # where the named tuple is created. Bypass this step in enviroments where
183
- # sys._getframe is not defined (Jython for example).
184
- if hasattr (_sys , '_getframe' ) and _sys .platform != 'cli' :
185
- result .__module__ = _sys ._getframe (1 ).f_globals ['__name__' ]
186
-
187
- return result
188
- else :
189
- from collections import namedtuple
35
+ __all__ = ['namedslice' , 'cachedproperty' ]
190
36
191
37
192
38
def namedslice (cls , obj ):
@@ -222,8 +68,6 @@ def namedslice(cls, obj):
222
68
return cls (* (getattr (obj , attr , None ) for attr in cls ._fields ))
223
69
224
70
225
- # Caching decorators
226
- __all__ .append ('cachedproperty' )
227
71
class cachedproperty (property ):
228
72
"""Convert a method into a cached property"""
229
73
@@ -239,89 +83,70 @@ def fget(s):
239
83
super (cachedproperty , self ).__init__ (fget )
240
84
241
85
242
- # Console/terminal size calculation (urgh!)
243
86
__all__ .append ('terminal_size' )
244
87
if sys .platform .startswith ('win' ):
245
- try :
246
- import win32console
247
- import win32api
248
- hstdin , hstdout , hstderr = win32api .STD_INPUT_HANDLE , win32api .STD_OUTPUT_HANDLE , win32api .STD_ERROR_HANDLE
249
- except ImportError :
250
- hstdin , hstdout , hstderr = - 10 , - 11 , - 12
251
- try :
252
- import ctypes
253
- import struct
254
- except ImportError :
255
- # If neither ctypes (Python 2.5+) nor PyWin32 (extension) is
256
- # available, simply default to 80x25
257
- def query_console_size (handle ):
258
- return None
259
- else :
260
- # ctypes query_console_size() adapted from
261
- # http://code.activestate.com/recipes/440694/
262
- def query_console_size (handle ):
263
- h = windll .kernel32 .GetStdHandle (handle )
264
- if h :
265
- buf = create_string_buffer (22 )
266
- if windll .kernel32 .GetConsoleScreenBufferInfo (h , buf ):
267
- (
268
- bufx , bufy ,
269
- curx , cury ,
270
- wattr ,
271
- left , top , right , bottom ,
272
- maxx , maxy ,
273
- ) = struct .unpack ('hhhhHhhhhhh' , buf .raw )
274
- return (right - left + 1 , bottom - top + 1 )
275
- return None
276
- else :
277
- # PyWin32 query_console_size() adapted from
278
- # http://groups.google.com/group/comp.lang.python/msg/f0febe6a8de9666b
279
- def query_console_size (handle ):
280
- try :
281
- csb = win32console .GetStdHandle (handle )
282
- csbi = csb .GetConsoleScreenBufferInfo ()
283
- size = csbi ['Window' ]
284
- return (size .Right - size .Left + 1 , size .Bottom - size .Top + 1 )
285
- except :
286
- return None
287
- def default_console_size ():
288
- return (80 , 25 )
88
+ # ctypes query_console_size() adapted from
89
+ # http://code.activestate.com/recipes/440694/
90
+ import ctypes
91
+
92
+ def terminal_size ():
93
+ "Returns the size (cols, rows) of the console"
94
+
95
+ def get_handle_size (handle ):
96
+ "Subroutine for querying terminal size from std handle"
97
+ handle = ctypes .windll .kernel32 .GetStdHandle (handle )
98
+ if handle :
99
+ buf = ctypes .create_string_buffer (22 )
100
+ if ctypes .windll .kernel32 .GetConsoleScreenBufferInfo (
101
+ handle , buf ):
102
+ (left , top , right , bottom ) = struct .unpack (
103
+ str ('hhhhHhhhhhh' ), buf .raw )[5 :9 ]
104
+ return (right - left + 1 , bottom - top + 1 )
105
+ return None
106
+
107
+ stdin , stdout , stderr = - 10 , - 11 , - 12
108
+ return (
109
+ get_handle_size (stderr ) or
110
+ get_handle_size (stdout ) or
111
+ get_handle_size (stdin ) or
112
+ # Default
113
+ (80 , 25 )
114
+ )
115
+
289
116
else :
290
- # POSIX query_console_size() adapted from
291
- # http://mail.python.org/pipermail/python-list/2006-February/365594.html
292
- # http://mail.python.org/pipermail/python-list/2000-May/033365.html
293
117
import fcntl
294
118
import termios
295
- import struct
296
119
import os
297
- hstdin , hstdout , hstderr = 0 , 1 , 2
298
- def query_console_size (handle ):
299
- try :
300
- buf = fcntl .ioctl (handle , termios .TIOCGWINSZ , '12345678' )
301
- row , col , rpx , cpx = struct .unpack ('hhhh' , buf )
302
- return (col , row )
303
- except :
304
- return None
305
- def default_console_size ():
306
- try :
120
+
121
+ def terminal_size ():
122
+ "Returns the size (cols, rows) of the console"
123
+
124
+ def get_handle_size (handle ):
125
+ "Subroutine for querying terminal size from std handle"
126
+ try :
127
+ buf = fcntl .ioctl (handle , termios .TIOCGWINSZ , '12345678' )
128
+ row , col = struct .unpack (str ('hhhh' ), buf )[0 :2 ]
129
+ return (col , row )
130
+ except IOError :
131
+ return None
132
+
133
+ stdin , stdout , stderr = 0 , 1 , 2
134
+ # Try stderr first as it's the least likely to be redirected
135
+ result = (
136
+ get_handle_size (stderr ) or
137
+ get_handle_size (stdout ) or
138
+ get_handle_size (stdin )
139
+ )
140
+ if not result :
307
141
fd = os .open (os .ctermid (), os .O_RDONLY )
308
142
try :
309
- result = query_console_size (fd )
143
+ result = get_handle_size (fd )
310
144
finally :
311
145
os .close (fd )
312
- except OSError :
313
- result = None
314
- if result :
315
- return result
316
- try :
317
- return (os .environ ['COLUMNS' ], os .environ ['LINES' ])
318
- except :
319
- return (80 , 25 )
320
-
321
- def terminal_size ():
322
- # Try stderr first as it's the least likely to be redirected
323
- for handle in (hstderr , hstdout , hstdin ):
324
- result = query_console_size (handle )
325
- if result :
326
- return result
327
- return default_console_size ()
146
+ if not result :
147
+ try :
148
+ result = (os .environ ['COLUMNS' ], os .environ ['LINES' ])
149
+ except KeyError :
150
+ # Default
151
+ result = (80 , 24 )
152
+ return result
0 commit comments