source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/pp.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 15.7 KB
Line 
1# == Pretty-printer for Ruby objects.
2#
3# = Which seems better?
4#
5# non-pretty-printed output by #p is:
6# #<PP:0x81fedf0 @genspace=#<Proc:0x81feda0>, @group_queue=#<PrettyPrint::GroupQueue:0x81fed3c @queue=[[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], []]>, @buffer=[], @newline="\n", @group_stack=[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#<IO:0x8114ee4>>
7#
8# pretty-printed output by #pp is:
9# #<PP:0x81fedf0
10# @buffer=[],
11# @buffer_width=0,
12# @genspace=#<Proc:0x81feda0>,
13# @group_queue=
14# #<PrettyPrint::GroupQueue:0x81fed3c
15# @queue=
16# [[#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
17# []]>,
18# @group_stack=
19# [#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
20# @indent=0,
21# @maxwidth=79,
22# @newline="\n",
23# @output=#<IO:0x8114ee4>,
24# @output_width=2>
25#
26# I like the latter. If you do too, this library is for you.
27#
28# = Usage
29#
30# pp(obj)
31#
32# output +obj+ to +$>+ in pretty printed format.
33#
34# It returns +nil+.
35#
36# = Output Customization
37# To define your customized pretty printing function for your classes,
38# redefine a method #pretty_print(+pp+) in the class.
39# It takes an argument +pp+ which is an instance of the class PP.
40# The method should use PP#text, PP#breakable, PP#nest, PP#group and
41# PP#pp to print the object.
42#
43# = Author
44# Tanaka Akira <[email protected]>
45
46require 'prettyprint'
47
48module Kernel
49 # returns a pretty printed object as a string.
50 def pretty_inspect
51 PP.pp(self, '')
52 end
53
54 private
55 # prints arguments in pretty form.
56 #
57 # pp returns nil.
58 def pp(*objs) # :doc:
59 objs.each {|obj|
60 PP.pp(obj)
61 }
62 nil
63 end
64 module_function :pp
65end
66
67class PP < PrettyPrint
68 # Outputs +obj+ to +out+ in pretty printed format of
69 # +width+ columns in width.
70 #
71 # If +out+ is omitted, +$>+ is assumed.
72 # If +width+ is omitted, 79 is assumed.
73 #
74 # PP.pp returns +out+.
75 def PP.pp(obj, out=$>, width=79)
76 q = PP.new(out, width)
77 q.guard_inspect_key {q.pp obj}
78 q.flush
79 #$pp = q
80 out << "\n"
81 end
82
83 # Outputs +obj+ to +out+ like PP.pp but with no indent and
84 # newline.
85 #
86 # PP.singleline_pp returns +out+.
87 def PP.singleline_pp(obj, out=$>)
88 q = SingleLine.new(out)
89 q.guard_inspect_key {q.pp obj}
90 q.flush
91 out
92 end
93
94 # :stopdoc:
95 def PP.mcall(obj, mod, meth, *args, &block)
96 mod.instance_method(meth).bind(obj).call(*args, &block)
97 end
98 # :startdoc:
99
100 @sharing_detection = false
101 class << self
102 # Returns the sharing detection flag as a boolean value.
103 # It is false by default.
104 attr_accessor :sharing_detection
105 end
106
107 module PPMethods
108 InspectKey = :__inspect_key__
109
110 def guard_inspect_key
111 if Thread.current[InspectKey] == nil
112 Thread.current[InspectKey] = []
113 end
114
115 save = Thread.current[InspectKey]
116
117 begin
118 Thread.current[InspectKey] = []
119 yield
120 ensure
121 Thread.current[InspectKey] = save
122 end
123 end
124
125 # Adds +obj+ to the pretty printing buffer
126 # using Object#pretty_print or Object#pretty_print_cycle.
127 #
128 # Object#pretty_print_cycle is used when +obj+ is already
129 # printed, a.k.a the object reference chain has a cycle.
130 def pp(obj)
131 id = obj.__id__
132
133 if Thread.current[InspectKey].include? id
134 group {obj.pretty_print_cycle self}
135 return
136 end
137
138 begin
139 Thread.current[InspectKey] << id
140 group {obj.pretty_print self}
141 ensure
142 Thread.current[InspectKey].pop unless PP.sharing_detection
143 end
144 end
145
146 # A convenience method which is same as follows:
147 #
148 # group(1, '#<' + obj.class.name, '>') { ... }
149 def object_group(obj, &block) # :yield:
150 group(1, '#<' + obj.class.name, '>', &block)
151 end
152
153 def object_address_group(obj, &block)
154 id = "%x" % (obj.__id__ * 2)
155 id.sub!(/\Af(?=[[:xdigit:]]{2}+\z)/, '') if id.sub!(/\A\.\./, '')
156 group(1, "\#<#{obj.class}:0x#{id}", '>', &block)
157 end
158
159 # A convenience method which is same as follows:
160 #
161 # text ','
162 # breakable
163 def comma_breakable
164 text ','
165 breakable
166 end
167
168 # Adds a separated list.
169 # The list is separated by comma with breakable space, by default.
170 #
171 # #seplist iterates the +list+ using +iter_method+.
172 # It yields each object to the block given for #seplist.
173 # The procedure +separator_proc+ is called between each yields.
174 #
175 # If the iteration is zero times, +separator_proc+ is not called at all.
176 #
177 # If +separator_proc+ is nil or not given,
178 # +lambda { comma_breakable }+ is used.
179 # If +iter_method+ is not given, :each is used.
180 #
181 # For example, following 3 code fragments has similar effect.
182 #
183 # q.seplist([1,2,3]) {|v| xxx v }
184 #
185 # q.seplist([1,2,3], lambda { comma_breakable }, :each) {|v| xxx v }
186 #
187 # xxx 1
188 # q.comma_breakable
189 # xxx 2
190 # q.comma_breakable
191 # xxx 3
192 def seplist(list, sep=nil, iter_method=:each) # :yield: element
193 sep ||= lambda { comma_breakable }
194 first = true
195 list.__send__(iter_method) {|*v|
196 if first
197 first = false
198 else
199 sep.call
200 end
201 yield(*v)
202 }
203 end
204
205 def pp_object(obj)
206 object_address_group(obj) {
207 seplist(obj.pretty_print_instance_variables, lambda { text ',' }) {|v|
208 breakable
209 v = v.to_s if Symbol === v
210 text v
211 text '='
212 group(1) {
213 breakable ''
214 pp(obj.instance_eval(v))
215 }
216 }
217 }
218 end
219
220 def pp_hash(obj)
221 group(1, '{', '}') {
222 seplist(obj, nil, :each_pair) {|k, v|
223 group {
224 pp k
225 text '=>'
226 group(1) {
227 breakable ''
228 pp v
229 }
230 }
231 }
232 }
233 end
234 end
235
236 include PPMethods
237
238 class SingleLine < PrettyPrint::SingleLine
239 include PPMethods
240 end
241
242 module ObjectMixin
243 # 1. specific pretty_print
244 # 2. specific inspect
245 # 3. specific to_s if instance variable is empty
246 # 4. generic pretty_print
247
248 # A default pretty printing method for general objects.
249 # It calls #pretty_print_instance_variables to list instance variables.
250 #
251 # If +self+ has a customized (redefined) #inspect method,
252 # the result of self.inspect is used but it obviously has no
253 # line break hints.
254 #
255 # This module provides predefined #pretty_print methods for some of
256 # the most commonly used built-in classes for convenience.
257 def pretty_print(q)
258 if /\(Kernel\)#/ !~ method(:inspect).inspect
259 q.text self.inspect
260 elsif /\(Kernel\)#/ !~ method(:to_s).inspect && instance_variables.empty?
261 q.text self.to_s
262 else
263 q.pp_object(self)
264 end
265 end
266
267 # A default pretty printing method for general objects that are
268 # detected as part of a cycle.
269 def pretty_print_cycle(q)
270 q.object_address_group(self) {
271 q.breakable
272 q.text '...'
273 }
274 end
275
276 # Returns a sorted array of instance variable names.
277 #
278 # This method should return an array of names of instance variables as symbols or strings as:
279 # +[:@a, :@b]+.
280 def pretty_print_instance_variables
281 instance_variables.sort
282 end
283
284 # Is #inspect implementation using #pretty_print.
285 # If you implement #pretty_print, it can be used as follows.
286 #
287 # alias inspect pretty_print_inspect
288 #
289 # However, doing this requires that every class that #inspect is called on
290 # implement #pretty_print, or a RuntimeError will be raised.
291 def pretty_print_inspect
292 if /\(PP::ObjectMixin\)#/ =~ method(:pretty_print).inspect
293 raise "pretty_print is not overridden for #{self.class}"
294 end
295 PP.singleline_pp(self, '')
296 end
297 end
298end
299
300class Array
301 def pretty_print(q)
302 q.group(1, '[', ']') {
303 q.seplist(self) {|v|
304 q.pp v
305 }
306 }
307 end
308
309 def pretty_print_cycle(q)
310 q.text(empty? ? '[]' : '[...]')
311 end
312end
313
314class Hash
315 def pretty_print(q)
316 q.pp_hash self
317 end
318
319 def pretty_print_cycle(q)
320 q.text(empty? ? '{}' : '{...}')
321 end
322end
323
324class << ENV
325 def pretty_print(q)
326 q.pp_hash self
327 end
328end
329
330class Struct
331 def pretty_print(q)
332 q.group(1, '#<struct ' + PP.mcall(self, Kernel, :class).name, '>') {
333 q.seplist(PP.mcall(self, Struct, :members), lambda { q.text "," }) {|member|
334 q.breakable
335 q.text member.to_s
336 q.text '='
337 q.group(1) {
338 q.breakable ''
339 q.pp self[member]
340 }
341 }
342 }
343 end
344
345 def pretty_print_cycle(q)
346 q.text sprintf("#<struct %s:...>", PP.mcall(self, Kernel, :class).name)
347 end
348end
349
350class Range
351 def pretty_print(q)
352 q.pp self.begin
353 q.breakable ''
354 q.text(self.exclude_end? ? '...' : '..')
355 q.breakable ''
356 q.pp self.end
357 end
358end
359
360class File
361 class Stat
362 def pretty_print(q)
363 require 'etc.so'
364 q.object_group(self) {
365 q.breakable
366 q.text sprintf("dev=0x%x", self.dev); q.comma_breakable
367 q.text "ino="; q.pp self.ino; q.comma_breakable
368 q.group {
369 m = self.mode
370 q.text sprintf("mode=0%o", m)
371 q.breakable
372 q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)",
373 self.ftype,
374 (m & 0400 == 0 ? ?- : ?r),
375 (m & 0200 == 0 ? ?- : ?w),
376 (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
377 (m & 04000 == 0 ? ?x : ?s)),
378 (m & 0040 == 0 ? ?- : ?r),
379 (m & 0020 == 0 ? ?- : ?w),
380 (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
381 (m & 02000 == 0 ? ?x : ?s)),
382 (m & 0004 == 0 ? ?- : ?r),
383 (m & 0002 == 0 ? ?- : ?w),
384 (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
385 (m & 01000 == 0 ? ?x : ?t)))
386 }
387 q.comma_breakable
388 q.text "nlink="; q.pp self.nlink; q.comma_breakable
389 q.group {
390 q.text "uid="; q.pp self.uid
391 begin
392 pw = Etc.getpwuid(self.uid)
393 rescue ArgumentError
394 end
395 if pw
396 q.breakable; q.text "(#{pw.name})"
397 end
398 }
399 q.comma_breakable
400 q.group {
401 q.text "gid="; q.pp self.gid
402 begin
403 gr = Etc.getgrgid(self.gid)
404 rescue ArgumentError
405 end
406 if gr
407 q.breakable; q.text "(#{gr.name})"
408 end
409 }
410 q.comma_breakable
411 q.group {
412 q.text sprintf("rdev=0x%x", self.rdev)
413 q.breakable
414 q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor)
415 }
416 q.comma_breakable
417 q.text "size="; q.pp self.size; q.comma_breakable
418 q.text "blksize="; q.pp self.blksize; q.comma_breakable
419 q.text "blocks="; q.pp self.blocks; q.comma_breakable
420 q.group {
421 t = self.atime
422 q.text "atime="; q.pp t
423 q.breakable; q.text "(#{t.tv_sec})"
424 }
425 q.comma_breakable
426 q.group {
427 t = self.mtime
428 q.text "mtime="; q.pp t
429 q.breakable; q.text "(#{t.tv_sec})"
430 }
431 q.comma_breakable
432 q.group {
433 t = self.ctime
434 q.text "ctime="; q.pp t
435 q.breakable; q.text "(#{t.tv_sec})"
436 }
437 }
438 end
439 end
440end
441
442class MatchData
443 def pretty_print(q)
444 q.object_group(self) {
445 q.breakable
446 q.seplist(1..self.size, lambda { q.breakable }) {|i|
447 q.pp self[i-1]
448 }
449 }
450 end
451end
452
453class Object
454 include PP::ObjectMixin
455end
456
457[Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c|
458 c.class_eval {
459 def pretty_print_cycle(q)
460 q.text inspect
461 end
462 }
463}
464
465[Numeric, FalseClass, TrueClass, Module].each {|c|
466 c.class_eval {
467 def pretty_print(q)
468 q.text inspect
469 end
470 }
471}
472
473# :enddoc:
474if __FILE__ == $0
475 require 'test/unit'
476
477 class PPTest < Test::Unit::TestCase
478 def test_list0123_12
479 assert_equal("[0, 1, 2, 3]\n", PP.pp([0,1,2,3], '', 12))
480 end
481
482 def test_list0123_11
483 assert_equal("[0,\n 1,\n 2,\n 3]\n", PP.pp([0,1,2,3], '', 11))
484 end
485
486 OverriddenStruct = Struct.new("OverriddenStruct", :members, :class)
487 def test_struct_override_members # [ruby-core:7865]
488 a = OverriddenStruct.new(1,2)
489 assert_equal("#<struct Struct::OverriddenStruct members=1, class=2>\n", PP.pp(a, ''))
490 end
491 end
492
493 class HasInspect
494 def initialize(a)
495 @a = a
496 end
497
498 def inspect
499 return "<inspect:#{@a.inspect}>"
500 end
501 end
502
503 class HasPrettyPrint
504 def initialize(a)
505 @a = a
506 end
507
508 def pretty_print(q)
509 q.text "<pretty_print:"
510 q.pp @a
511 q.text ">"
512 end
513 end
514
515 class HasBoth
516 def initialize(a)
517 @a = a
518 end
519
520 def inspect
521 return "<inspect:#{@a.inspect}>"
522 end
523
524 def pretty_print(q)
525 q.text "<pretty_print:"
526 q.pp @a
527 q.text ">"
528 end
529 end
530
531 class PrettyPrintInspect < HasPrettyPrint
532 alias inspect pretty_print_inspect
533 end
534
535 class PrettyPrintInspectWithoutPrettyPrint
536 alias inspect pretty_print_inspect
537 end
538
539 class PPInspectTest < Test::Unit::TestCase
540 def test_hasinspect
541 a = HasInspect.new(1)
542 assert_equal("<inspect:1>\n", PP.pp(a, ''))
543 end
544
545 def test_hasprettyprint
546 a = HasPrettyPrint.new(1)
547 assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
548 end
549
550 def test_hasboth
551 a = HasBoth.new(1)
552 assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
553 end
554
555 def test_pretty_print_inspect
556 a = PrettyPrintInspect.new(1)
557 assert_equal("<pretty_print:1>", a.inspect)
558 a = PrettyPrintInspectWithoutPrettyPrint.new
559 assert_raise(RuntimeError) { a.inspect }
560 end
561
562 def test_proc
563 a = proc {1}
564 assert_equal("#{a.inspect}\n", PP.pp(a, ''))
565 end
566
567 def test_to_s_with_iv
568 a = Object.new
569 def a.to_s() "aaa" end
570 a.instance_eval { @a = nil }
571 result = PP.pp(a, '')
572 assert_equal("#{a.inspect}\n", result)
573 assert_match(/\A#<Object.*>\n\z/m, result)
574 a = 1.0
575 a.instance_eval { @a = nil }
576 result = PP.pp(a, '')
577 assert_equal("#{a.inspect}\n", result)
578 end
579
580 def test_to_s_without_iv
581 a = Object.new
582 def a.to_s() "aaa" end
583 result = PP.pp(a, '')
584 assert_equal("#{a.inspect}\n", result)
585 assert_equal("aaa\n", result)
586 end
587 end
588
589 class PPCycleTest < Test::Unit::TestCase
590 def test_array
591 a = []
592 a << a
593 assert_equal("[[...]]\n", PP.pp(a, ''))
594 assert_equal("#{a.inspect}\n", PP.pp(a, ''))
595 end
596
597 def test_hash
598 a = {}
599 a[0] = a
600 assert_equal("{0=>{...}}\n", PP.pp(a, ''))
601 assert_equal("#{a.inspect}\n", PP.pp(a, ''))
602 end
603
604 S = Struct.new("S", :a, :b)
605 def test_struct
606 a = S.new(1,2)
607 a.b = a
608 assert_equal("#<struct Struct::S a=1, b=#<struct Struct::S:...>>\n", PP.pp(a, ''))
609 assert_equal("#{a.inspect}\n", PP.pp(a, ''))
610 end
611
612 def test_object
613 a = Object.new
614 a.instance_eval {@a = a}
615 assert_equal(a.inspect + "\n", PP.pp(a, ''))
616 end
617
618 def test_anonymous
619 a = Class.new.new
620 assert_equal(a.inspect + "\n", PP.pp(a, ''))
621 end
622
623 def test_withinspect
624 a = []
625 a << HasInspect.new(a)
626 assert_equal("[<inspect:[...]>]\n", PP.pp(a, ''))
627 assert_equal("#{a.inspect}\n", PP.pp(a, ''))
628 end
629
630 def test_share_nil
631 begin
632 PP.sharing_detection = true
633 a = [nil, nil]
634 assert_equal("[nil, nil]\n", PP.pp(a, ''))
635 ensure
636 PP.sharing_detection = false
637 end
638 end
639 end
640
641 class PPSingleLineTest < Test::Unit::TestCase
642 def test_hash
643 assert_equal("{1=>1}", PP.singleline_pp({ 1 => 1}, '')) # [ruby-core:02699]
644 assert_equal("[1#{', 1'*99}]", PP.singleline_pp([1]*100, ''))
645 end
646 end
647end
Note: See TracBrowser for help on using the repository browser.