Tag unstable CGI specs.
[rbx.git] / lib / pp.rb
blobeec86195f4f77dd492cb6968e371ad44e5e7deb6
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)
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.
43 # = Author
44 # Tanaka Akira <akr@m17n.org>
46 require 'prettyprint'
48 module Kernel
49   # returns a pretty printed object as a string.
50   def pretty_inspect
51     PP.pp(self, '')
52   end
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
65 end
67 class 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
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
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:
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
107   module PPMethods
108     InspectKey = :__inspect_key__
110     def guard_inspect_key
111       if Thread.current[InspectKey] == nil
112         Thread.current[InspectKey] = []
113       end
115       save = Thread.current[InspectKey]
117       begin
118         Thread.current[InspectKey] = []
119         yield
120       ensure
121         Thread.current[InspectKey] = save
122       end
123     end
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__
133       if Thread.current[InspectKey].include? id
134         group {obj.pretty_print_cycle self}
135         return
136       end
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
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
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
159     # A convenience method which is same as follows:
160     # 
161     #   text ','
162     #   breakable
163     def comma_breakable
164       text ','
165       breakable
166     end
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
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
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
236   include PPMethods
238   class SingleLine < PrettyPrint::SingleLine
239     include PPMethods
240   end
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
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
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
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
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
300 class Array
301   def pretty_print(q)
302     q.group(1, '[', ']') {
303       q.seplist(self) {|v|
304         q.pp v
305       }
306     }
307   end
309   def pretty_print_cycle(q)
310     q.text(empty? ? '[]' : '[...]')
311   end
314 class Hash
315   def pretty_print(q)
316     q.pp_hash self
317   end
319   def pretty_print_cycle(q)
320     q.text(empty? ? '{}' : '{...}')
321   end
324 class << ENV
325   def pretty_print(q)
326     q.pp_hash self
327   end
330 class 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
345   def pretty_print_cycle(q)
346     q.text sprintf("#<struct %s:...>", PP.mcall(self, Kernel, :class).name)
347   end
350 class 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
360 class File
361   class Stat
362     def pretty_print(q)
363       q.object_group(self) {
364         q.breakable
365         q.text sprintf("dev=0x%x", self.dev); q.comma_breakable
366         q.text "ino="; q.pp self.ino; q.comma_breakable
367         q.group {
368           m = self.mode
369           q.text sprintf("mode=0%o", m)
370           q.breakable
371           q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)",
372             self.ftype,
373             (m & 0400 == 0 ? ?- : ?r),
374             (m & 0200 == 0 ? ?- : ?w),
375             (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
376                              (m & 04000 == 0 ? ?x : ?s)),
377             (m & 0040 == 0 ? ?- : ?r),
378             (m & 0020 == 0 ? ?- : ?w),
379             (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
380                              (m & 02000 == 0 ? ?x : ?s)),
381             (m & 0004 == 0 ? ?- : ?r),
382             (m & 0002 == 0 ? ?- : ?w),
383             (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
384                              (m & 01000 == 0 ? ?x : ?t)))
385         }
386         q.comma_breakable
387         q.text "nlink="; q.pp self.nlink; q.comma_breakable
388         q.group {
389           q.text "uid="; q.pp self.uid
390           begin
391             pw = Etc.getpwuid(self.uid)
392           rescue ArgumentError
393           end
394           if pw
395             q.breakable; q.text "(#{pw.name})"
396           end
397         }
398         q.comma_breakable
399         q.group {
400           q.text "gid="; q.pp self.gid
401           begin
402             gr = Etc.getgrgid(self.gid)
403           rescue ArgumentError
404           end
405           if gr
406             q.breakable; q.text "(#{gr.name})"
407           end
408         }
409         q.comma_breakable
410         q.group {
411           q.text sprintf("rdev=0x%x", self.rdev)
412           q.breakable
413           q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor)
414         }
415         q.comma_breakable
416         q.text "size="; q.pp self.size; q.comma_breakable
417         q.text "blksize="; q.pp self.blksize; q.comma_breakable
418         q.text "blocks="; q.pp self.blocks; q.comma_breakable
419         q.group {
420           t = self.atime
421           q.text "atime="; q.pp t
422           q.breakable; q.text "(#{t.tv_sec})"
423         }
424         q.comma_breakable
425         q.group {
426           t = self.mtime
427           q.text "mtime="; q.pp t
428           q.breakable; q.text "(#{t.tv_sec})"
429         }
430         q.comma_breakable
431         q.group {
432           t = self.ctime
433           q.text "ctime="; q.pp t
434           q.breakable; q.text "(#{t.tv_sec})"
435         }
436       }
437     end
438   end
441 class MatchData
442   def pretty_print(q)
443     q.object_group(self) {
444       q.breakable
445       q.seplist(1..self.size, lambda { q.breakable }) {|i|
446         q.pp self[i-1]
447       }
448     }
449   end
452 class Object
453   include PP::ObjectMixin
456 [Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c|
457   c.class_eval {
458     def pretty_print_cycle(q)
459       q.text inspect
460     end
461   }
464 [Numeric, FalseClass, TrueClass, Module].each {|c|
465   c.class_eval {
466     def pretty_print(q)
467       q.text inspect
468     end
469   }
472 # :enddoc:
473 if __FILE__ == $0
474   require 'test/unit'
476   class PPTest < Test::Unit::TestCase
477     def test_list0123_12
478       assert_equal("[0, 1, 2, 3]\n", PP.pp([0,1,2,3], '', 12))
479     end
481     def test_list0123_11
482       assert_equal("[0,\n 1,\n 2,\n 3]\n", PP.pp([0,1,2,3], '', 11))
483     end
485     OverriddenStruct = Struct.new("OverriddenStruct", :members, :class)
486     def test_struct_override_members # [ruby-core:7865]
487       a = OverriddenStruct.new(1,2)
488       assert_equal("#<struct Struct::OverriddenStruct members=1, class=2>\n", PP.pp(a, ''))
489     end
490   end
492   class HasInspect
493     def initialize(a)
494       @a = a
495     end
497     def inspect
498       return "<inspect:#{@a.inspect}>"
499     end
500   end
502   class HasPrettyPrint
503     def initialize(a)
504       @a = a
505     end
507     def pretty_print(q)
508       q.text "<pretty_print:"
509       q.pp @a
510       q.text ">"
511     end
512   end
514   class HasBoth
515     def initialize(a)
516       @a = a
517     end
519     def inspect
520       return "<inspect:#{@a.inspect}>"
521     end
523     def pretty_print(q)
524       q.text "<pretty_print:"
525       q.pp @a
526       q.text ">"
527     end
528   end
530   class PrettyPrintInspect < HasPrettyPrint
531     alias inspect pretty_print_inspect
532   end
534   class PrettyPrintInspectWithoutPrettyPrint
535     alias inspect pretty_print_inspect
536   end
538   class PPInspectTest < Test::Unit::TestCase
539     def test_hasinspect
540       a = HasInspect.new(1)
541       assert_equal("<inspect:1>\n", PP.pp(a, ''))
542     end
544     def test_hasprettyprint
545       a = HasPrettyPrint.new(1)
546       assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
547     end
549     def test_hasboth
550       a = HasBoth.new(1)
551       assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
552     end
554     def test_pretty_print_inspect
555       a = PrettyPrintInspect.new(1)
556       assert_equal("<pretty_print:1>", a.inspect)
557       a = PrettyPrintInspectWithoutPrettyPrint.new
558       assert_raise(RuntimeError) { a.inspect }
559     end
561     def test_proc
562       a = proc {1}
563       assert_equal("#{a.inspect}\n", PP.pp(a, ''))
564     end
566     def test_to_s_with_iv
567       a = Object.new
568       def a.to_s() "aaa" end
569       a.instance_eval { @a = nil }
570       result = PP.pp(a, '')
571       assert_equal("#{a.inspect}\n", result)
572       assert_match(/\A#<Object.*>\n\z/m, result)
573       a = 1.0
574       a.instance_eval { @a = nil }
575       result = PP.pp(a, '')
576       assert_equal("#{a.inspect}\n", result)
577     end
578     
579     def test_to_s_without_iv
580       a = Object.new
581       def a.to_s() "aaa" end
582       result = PP.pp(a, '')
583       assert_equal("#{a.inspect}\n", result)
584       assert_equal("aaa\n", result)
585     end
586   end
588   class PPCycleTest < Test::Unit::TestCase
589     def test_array
590       a = []
591       a << a
592       assert_equal("[[...]]\n", PP.pp(a, ''))
593       assert_equal("#{a.inspect}\n", PP.pp(a, ''))
594     end
596     def test_hash
597       a = {}
598       a[0] = a
599       assert_equal("{0=>{...}}\n", PP.pp(a, ''))
600       assert_equal("#{a.inspect}\n", PP.pp(a, ''))
601     end
603     S = Struct.new("S", :a, :b)
604     def test_struct
605       a = S.new(1,2)
606       a.b = a
607       assert_equal("#<struct Struct::S a=1, b=#<struct Struct::S:...>>\n", PP.pp(a, ''))
608       assert_equal("#{a.inspect}\n", PP.pp(a, ''))
609     end
611     def test_object
612       a = Object.new
613       a.instance_eval {@a = a}
614       assert_equal(a.inspect + "\n", PP.pp(a, ''))
615     end
617     def test_anonymous
618       a = Class.new.new
619       assert_equal(a.inspect + "\n", PP.pp(a, ''))
620     end
622     def test_withinspect
623       a = []
624       a << HasInspect.new(a)
625       assert_equal("[<inspect:[...]>]\n", PP.pp(a, ''))
626       assert_equal("#{a.inspect}\n", PP.pp(a, ''))
627     end
629     def test_share_nil
630       begin
631         PP.sharing_detection = true
632         a = [nil, nil]
633         assert_equal("[nil, nil]\n", PP.pp(a, ''))
634       ensure
635         PP.sharing_detection = false
636       end
637     end
638   end
640   class PPSingleLineTest < Test::Unit::TestCase
641     def test_hash
642       assert_equal("{1=>1}", PP.singleline_pp({ 1 => 1}, '')) # [ruby-core:02699]
643       assert_equal("[1#{', 1'*99}]", PP.singleline_pp([1]*100, ''))
644     end
645   end