blob: 530bc79d5c819a482fc3b4732068b3bff9f997f6 [file] [log] [blame]
Antoine Pitroua69ba652010-01-18 21:20:53 +00001# -*- coding: utf-8 -*-
2# This file should be kept compatible with both Python 2.6 and Python >= 3.0.
3
Victor Stinner8f23be72011-12-18 21:38:51 +01004import itertools
Antoine Pitroua69ba652010-01-18 21:20:53 +00005import os
Victor Stinner8f23be72011-12-18 21:38:51 +01006import platform
Antoine Pitroua69ba652010-01-18 21:20:53 +00007import re
8import sys
Victor Stinner8f23be72011-12-18 21:38:51 +01009import time
Antoine Pitroua69ba652010-01-18 21:20:53 +000010from optparse import OptionParser
11
12out = sys.stdout
13
14TEXT_ENCODING = 'utf8'
15NEWLINES = 'lf'
16
17# Compatibility
18try:
19 xrange
20except NameError:
21 xrange = range
22
23def text_open(fn, mode, encoding=None):
24 try:
25 return open(fn, mode, encoding=encoding or TEXT_ENCODING)
26 except TypeError:
27 return open(fn, mode)
28
29def get_file_sizes():
30 for s in ['20 KB', '400 KB', '10 MB']:
31 size, unit = s.split()
32 size = int(size) * {'KB': 1024, 'MB': 1024 ** 2}[unit]
33 yield s.replace(' ', ''), size
34
35def get_binary_files():
36 return ((name + ".bin", size) for name, size in get_file_sizes())
37
38def get_text_files():
39 return (("%s-%s-%s.txt" % (name, TEXT_ENCODING, NEWLINES), size)
40 for name, size in get_file_sizes())
41
42def with_open_mode(mode):
43 def decorate(f):
44 f.file_open_mode = mode
45 return f
46 return decorate
47
48def with_sizes(*sizes):
49 def decorate(f):
50 f.file_sizes = sizes
51 return f
52 return decorate
53
54
55# Here begin the tests
56
57@with_open_mode("r")
58@with_sizes("medium")
59def read_bytewise(f):
60 """ read one unit at a time """
61 f.seek(0)
62 while f.read(1):
63 pass
64
65@with_open_mode("r")
66@with_sizes("medium")
67def read_small_chunks(f):
68 """ read 20 units at a time """
69 f.seek(0)
70 while f.read(20):
71 pass
72
73@with_open_mode("r")
74@with_sizes("medium")
75def read_big_chunks(f):
76 """ read 4096 units at a time """
77 f.seek(0)
78 while f.read(4096):
79 pass
80
81@with_open_mode("r")
82@with_sizes("small", "medium", "large")
83def read_whole_file(f):
84 """ read whole contents at once """
85 f.seek(0)
86 while f.read():
87 pass
88
89@with_open_mode("rt")
90@with_sizes("medium")
91def read_lines(f):
92 """ read one line at a time """
93 f.seek(0)
94 for line in f:
95 pass
96
97@with_open_mode("r")
98@with_sizes("medium")
99def seek_forward_bytewise(f):
100 """ seek forward one unit at a time """
101 f.seek(0, 2)
102 size = f.tell()
103 f.seek(0, 0)
104 for i in xrange(0, size - 1):
105 f.seek(i, 0)
106
107@with_open_mode("r")
108@with_sizes("medium")
109def seek_forward_blockwise(f):
110 """ seek forward 1000 units at a time """
111 f.seek(0, 2)
112 size = f.tell()
113 f.seek(0, 0)
114 for i in xrange(0, size - 1, 1000):
115 f.seek(i, 0)
116
117@with_open_mode("rb")
118@with_sizes("medium")
119def read_seek_bytewise(f):
120 """ alternate read & seek one unit """
121 f.seek(0)
122 while f.read(1):
123 f.seek(1, 1)
124
125@with_open_mode("rb")
126@with_sizes("medium")
127def read_seek_blockwise(f):
128 """ alternate read & seek 1000 units """
129 f.seek(0)
130 while f.read(1000):
131 f.seek(1000, 1)
132
133
134@with_open_mode("w")
135@with_sizes("small")
136def write_bytewise(f, source):
137 """ write one unit at a time """
138 for i in xrange(0, len(source)):
139 f.write(source[i:i+1])
140
141@with_open_mode("w")
142@with_sizes("medium")
143def write_small_chunks(f, source):
144 """ write 20 units at a time """
145 for i in xrange(0, len(source), 20):
146 f.write(source[i:i+20])
147
148@with_open_mode("w")
149@with_sizes("medium")
150def write_medium_chunks(f, source):
151 """ write 4096 units at a time """
152 for i in xrange(0, len(source), 4096):
153 f.write(source[i:i+4096])
154
155@with_open_mode("w")
156@with_sizes("large")
157def write_large_chunks(f, source):
158 """ write 1e6 units at a time """
159 for i in xrange(0, len(source), 1000000):
160 f.write(source[i:i+1000000])
161
162
163@with_open_mode("w+")
164@with_sizes("small")
165def modify_bytewise(f, source):
166 """ modify one unit at a time """
167 f.seek(0)
168 for i in xrange(0, len(source)):
169 f.write(source[i:i+1])
170
171@with_open_mode("w+")
172@with_sizes("medium")
173def modify_small_chunks(f, source):
174 """ modify 20 units at a time """
175 f.seek(0)
176 for i in xrange(0, len(source), 20):
177 f.write(source[i:i+20])
178
179@with_open_mode("w+")
180@with_sizes("medium")
181def modify_medium_chunks(f, source):
182 """ modify 4096 units at a time """
183 f.seek(0)
184 for i in xrange(0, len(source), 4096):
185 f.write(source[i:i+4096])
186
187@with_open_mode("wb+")
188@with_sizes("medium")
189def modify_seek_forward_bytewise(f, source):
190 """ alternate write & seek one unit """
191 f.seek(0)
192 for i in xrange(0, len(source), 2):
193 f.write(source[i:i+1])
194 f.seek(i+2)
195
196@with_open_mode("wb+")
197@with_sizes("medium")
198def modify_seek_forward_blockwise(f, source):
199 """ alternate write & seek 1000 units """
200 f.seek(0)
201 for i in xrange(0, len(source), 2000):
202 f.write(source[i:i+1000])
203 f.seek(i+2000)
204
205# XXX the 2 following tests don't work with py3k's text IO
206@with_open_mode("wb+")
207@with_sizes("medium")
208def read_modify_bytewise(f, source):
209 """ alternate read & write one unit """
210 f.seek(0)
211 for i in xrange(0, len(source), 2):
212 f.read(1)
213 f.write(source[i+1:i+2])
214
215@with_open_mode("wb+")
216@with_sizes("medium")
217def read_modify_blockwise(f, source):
218 """ alternate read & write 1000 units """
219 f.seek(0)
220 for i in xrange(0, len(source), 2000):
221 f.read(1000)
222 f.write(source[i+1000:i+2000])
223
224
225read_tests = [
226 read_bytewise, read_small_chunks, read_lines, read_big_chunks,
227 None, read_whole_file, None,
228 seek_forward_bytewise, seek_forward_blockwise,
229 read_seek_bytewise, read_seek_blockwise,
230]
231
232write_tests = [
233 write_bytewise, write_small_chunks, write_medium_chunks, write_large_chunks,
234]
235
236modify_tests = [
237 modify_bytewise, modify_small_chunks, modify_medium_chunks,
238 None,
239 modify_seek_forward_bytewise, modify_seek_forward_blockwise,
240 read_modify_bytewise, read_modify_blockwise,
241]
242
243def run_during(duration, func):
244 _t = time.time
245 n = 0
246 start = os.times()
247 start_timestamp = _t()
248 real_start = start[4] or start_timestamp
249 while True:
250 func()
251 n += 1
252 if _t() - start_timestamp > duration:
253 break
254 end = os.times()
255 real = (end[4] if start[4] else time.time()) - real_start
256 return n, real, sum(end[0:2]) - sum(start[0:2])
257
258def warm_cache(filename):
259 with open(filename, "rb") as f:
260 f.read()
261
262
263def run_all_tests(options):
264 def print_label(filename, func):
265 name = re.split(r'[-.]', filename)[0]
266 out.write(
267 ("[%s] %s... "
268 % (name.center(7), func.__doc__.strip())
269 ).ljust(52))
270 out.flush()
271
272 def print_results(size, n, real, cpu):
273 bw = n * float(size) / 1024 ** 2 / real
274 bw = ("%4d MB/s" if bw > 100 else "%.3g MB/s") % bw
275 out.write(bw.rjust(12) + "\n")
276 if cpu < 0.90 * real:
277 out.write(" warning: test above used only %d%% CPU, "
278 "result may be flawed!\n" % (100.0 * cpu / real))
279
280 def run_one_test(name, size, open_func, test_func, *args):
281 mode = test_func.file_open_mode
282 print_label(name, test_func)
283 if "w" not in mode or "+" in mode:
284 warm_cache(name)
285 with open_func(name) as f:
286 n, real, cpu = run_during(1.5, lambda: test_func(f, *args))
287 print_results(size, n, real, cpu)
288
289 def run_test_family(tests, mode_filter, files, open_func, *make_args):
290 for test_func in tests:
291 if test_func is None:
292 out.write("\n")
293 continue
294 if mode_filter in test_func.file_open_mode:
295 continue
296 for s in test_func.file_sizes:
297 name, size = files[size_names[s]]
298 #name += file_ext
299 args = tuple(f(name, size) for f in make_args)
300 run_one_test(name, size,
301 open_func, test_func, *args)
302
303 size_names = {
304 "small": 0,
305 "medium": 1,
306 "large": 2,
307 }
308
Victor Stinner8f23be72011-12-18 21:38:51 +0100309 print("Python %s" % sys.version)
310 if sys.version_info < (3, 3):
311 if sys.maxunicode > 0xffff:
312 text = "UCS-4 (wide build)"
313 else:
314 text = "UTF-16 (narrow build)"
315 else:
316 text = "PEP 393"
317 print("Unicode: %s" % text)
318 print(platform.platform())
Antoine Pitroua69ba652010-01-18 21:20:53 +0000319 binary_files = list(get_binary_files())
320 text_files = list(get_text_files())
321 if "b" in options:
322 print("Binary unit = one byte")
323 if "t" in options:
324 print("Text unit = one character (%s-decoded)" % TEXT_ENCODING)
325
326 # Binary reads
327 if "b" in options and "r" in options:
328 print("\n** Binary input **\n")
329 run_test_family(read_tests, "t", binary_files, lambda fn: open(fn, "rb"))
330
331 # Text reads
332 if "t" in options and "r" in options:
333 print("\n** Text input **\n")
334 run_test_family(read_tests, "b", text_files, lambda fn: text_open(fn, "r"))
335
336 # Binary writes
337 if "b" in options and "w" in options:
338 print("\n** Binary append **\n")
339 def make_test_source(name, size):
340 with open(name, "rb") as f:
341 return f.read()
342 run_test_family(write_tests, "t", binary_files,
343 lambda fn: open(os.devnull, "wb"), make_test_source)
344
345 # Text writes
346 if "t" in options and "w" in options:
347 print("\n** Text append **\n")
348 def make_test_source(name, size):
349 with text_open(name, "r") as f:
350 return f.read()
351 run_test_family(write_tests, "b", text_files,
352 lambda fn: text_open(os.devnull, "w"), make_test_source)
353
354 # Binary overwrites
355 if "b" in options and "w" in options:
356 print("\n** Binary overwrite **\n")
357 def make_test_source(name, size):
358 with open(name, "rb") as f:
359 return f.read()
360 run_test_family(modify_tests, "t", binary_files,
361 lambda fn: open(fn, "r+b"), make_test_source)
362
363 # Text overwrites
364 if "t" in options and "w" in options:
365 print("\n** Text overwrite **\n")
366 def make_test_source(name, size):
367 with text_open(name, "r") as f:
368 return f.read()
369 run_test_family(modify_tests, "b", text_files,
Antoine Pitrou74472a92011-10-08 19:40:04 +0200370 lambda fn: text_open(fn, "r+"), make_test_source)
Antoine Pitroua69ba652010-01-18 21:20:53 +0000371
372
373def prepare_files():
374 print("Preparing files...")
375 # Binary files
376 for name, size in get_binary_files():
377 if os.path.isfile(name) and os.path.getsize(name) == size:
378 continue
379 with open(name, "wb") as f:
380 f.write(os.urandom(size))
381 # Text files
382 chunk = []
383 with text_open(__file__, "rU", encoding='utf8') as f:
384 for line in f:
385 if line.startswith("# <iobench text chunk marker>"):
386 break
387 else:
388 raise RuntimeError(
389 "Couldn't find chunk marker in %s !" % __file__)
390 if NEWLINES == "all":
391 it = itertools.cycle(["\n", "\r", "\r\n"])
392 else:
393 it = itertools.repeat(
394 {"cr": "\r", "lf": "\n", "crlf": "\r\n"}[NEWLINES])
395 chunk = "".join(line.replace("\n", next(it)) for line in f)
396 if isinstance(chunk, bytes):
397 chunk = chunk.decode('utf8')
398 chunk = chunk.encode(TEXT_ENCODING)
399 for name, size in get_text_files():
400 if os.path.isfile(name) and os.path.getsize(name) == size:
401 continue
402 head = chunk * (size // len(chunk))
403 tail = chunk[:size % len(chunk)]
404 # Adjust tail to end on a character boundary
405 while True:
406 try:
407 tail.decode(TEXT_ENCODING)
408 break
409 except UnicodeDecodeError:
410 tail = tail[:-1]
411 with open(name, "wb") as f:
412 f.write(head)
413 f.write(tail)
414
415def main():
416 global TEXT_ENCODING, NEWLINES
417
418 usage = "usage: %prog [-h|--help] [options]"
419 parser = OptionParser(usage=usage)
420 parser.add_option("-b", "--binary",
421 action="store_true", dest="binary", default=False,
422 help="run binary I/O tests")
423 parser.add_option("-t", "--text",
424 action="store_true", dest="text", default=False,
425 help="run text I/O tests")
426 parser.add_option("-r", "--read",
427 action="store_true", dest="read", default=False,
428 help="run read tests")
429 parser.add_option("-w", "--write",
430 action="store_true", dest="write", default=False,
431 help="run write & modify tests")
432 parser.add_option("-E", "--encoding",
433 action="store", dest="encoding", default=None,
434 help="encoding for text tests (default: %s)" % TEXT_ENCODING)
435 parser.add_option("-N", "--newlines",
436 action="store", dest="newlines", default='lf',
437 help="line endings for text tests "
438 "(one of: {lf (default), cr, crlf, all})")
Antoine Pitrou92f87f72010-09-06 12:36:55 +0000439 parser.add_option("-m", "--io-module",
440 action="store", dest="io_module", default=None,
441 help="io module to test (default: builtin open())")
Antoine Pitroua69ba652010-01-18 21:20:53 +0000442 options, args = parser.parse_args()
443 if args:
444 parser.error("unexpected arguments")
445 NEWLINES = options.newlines.lower()
446 if NEWLINES not in ('lf', 'cr', 'crlf', 'all'):
447 parser.error("invalid 'newlines' option: %r" % NEWLINES)
448
449 test_options = ""
450 if options.read:
451 test_options += "r"
452 if options.write:
453 test_options += "w"
454 elif not options.read:
455 test_options += "rw"
456 if options.text:
457 test_options += "t"
458 if options.binary:
459 test_options += "b"
460 elif not options.text:
461 test_options += "tb"
462
463 if options.encoding:
464 TEXT_ENCODING = options.encoding
465
Antoine Pitrou92f87f72010-09-06 12:36:55 +0000466 if options.io_module:
467 globals()['open'] = __import__(options.io_module, {}, {}, ['open']).open
468
Antoine Pitroua69ba652010-01-18 21:20:53 +0000469 prepare_files()
470 run_all_tests(test_options)
471
472if __name__ == "__main__":
473 main()
474
475
476# -- This part to exercise text reading. Don't change anything! --
477# <iobench text chunk marker>
478
479"""
4801.
481Gáttir allar,
482áðr gangi fram,
483um skoðask skyli,
484um skyggnast skyli,
485því at óvíst er at vita,
486hvar óvinir
487sitja á fleti fyrir.
488
4892.
490Gefendr heilir!
491Gestr er inn kominn,
492hvar skal sitja sjá?
493Mjök er bráðr,
494sá er á bröndum skal
495síns of freista frama.
496
4973.
498Elds er þörf,
499þeims inn er kominn
500ok á kné kalinn;
501matar ok váða
502er manni þörf,
503þeim er hefr um fjall farit.
504
5054.
506Vatns er þörf,
507þeim er til verðar kemr,
508þerru ok þjóðlaðar,
509góðs of æðis,
510ef sér geta mætti,
511orðs ok endrþögu.
512
5135.
514Vits er þörf,
515þeim er víða ratar;
516dælt er heima hvat;
517at augabragði verðr,
518sá er ekki kann
519ok með snotrum sitr.
520
5216.
522At hyggjandi sinni
523skyli-t maðr hræsinn vera,
524heldr gætinn at geði;
525þá er horskr ok þögull
526kemr heimisgarða til,
527sjaldan verðr víti vörum,
528því at óbrigðra vin
529fær maðr aldregi
530en mannvit mikit.
531
5327.
533Inn vari gestr,
534er til verðar kemr,
535þunnu hljóði þegir,
536eyrum hlýðir,
537en augum skoðar;
538svá nýsisk fróðra hverr fyrir.
539
5408.
541Hinn er sæll,
542er sér of getr
543lof ok líknstafi;
544ódælla er við þat,
545er maðr eiga skal
546annars brjóstum í.
547"""
548
549"""
550C'est revenir tard, je le sens, sur un sujet trop rebattu et déjà presque oublié. Mon état, qui ne me permet plus aucun travail suivi, mon aversion pour le genre polémique, ont causé ma lenteur à écrire et ma répugnance à publier. J'aurais même tout à fait supprimé ces Lettres, ou plutôt je lie les aurais point écrites, s'il n'eût été question que de moi : Mais ma patrie ne m'est pas tellement devenue étrangère que je puisse voir tranquillement opprimer ses citoyens, surtout lorsqu'ils n'ont compromis leurs droits qu'en défendant ma cause. Je serais le dernier des hommes si dans une telle occasion j'écoutais un sentiment qui n'est plus ni douceur ni patience, mais faiblesse et lâcheté, dans celui qu'il empêche de remplir son devoir.
551Rien de moins important pour le public, j'en conviens, que la matière de ces lettres. La constitution d'une petite République, le sort d'un petit particulier, l'exposé de quelques injustices, la réfutation de quelques sophismes ; tout cela n'a rien en soi d'assez considérable pour mériter beaucoup de lecteurs : mais si mes sujets sont petits mes objets sont grands, et dignes de l'attention de tout honnête homme. Laissons Genève à sa place, et Rousseau dans sa dépression ; mais la religion, mais la liberté, la justice ! voilà, qui que vous soyez, ce qui n'est pas au-dessous de vous.
552Qu'on ne cherche pas même ici dans le style le dédommagement de l'aridité de la matière. Ceux que quelques traits heureux de ma plume ont si fort irrités trouveront de quoi s'apaiser dans ces lettres, L'honneur de défendre un opprimé eût enflammé mon coeur si j'avais parlé pour un autre. Réduit au triste emploi de me défendre moi-même, j'ai dû me borner à raisonner ; m'échauffer eût été m'avilir. J'aurai donc trouvé grâce en ce point devant ceux qui s'imaginent qu'il est essentiel à la vérité d'être dite froidement ; opinion que pourtant j'ai peine à comprendre. Lorsqu'une vive persuasion nous anime, le moyen d'employer un langage glacé ? Quand Archimède tout transporté courait nu dans les rues de Syracuse, en avait-il moins trouvé la vérité parce qu'il se passionnait pour elle ? Tout au contraire, celui qui la sent ne peut s'abstenir de l'adorer ; celui qui demeure froid ne l'a pas vue.
553Quoi qu'il en soit, je prie les lecteurs de vouloir bien mettre à part mon beau style, et d'examiner seulement si je raisonne bien ou mal ; car enfin, de cela seul qu'un auteur s'exprime en bons termes, je ne vois pas comment il peut s'ensuivre que cet auteur ne sait ce qu'il dit.
554"""