PageRenderTime 190ms CodeModel.GetById 139ms app.highlight 44ms RepoModel.GetById 3ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/pp.rb

http://github.com/agross/netopenspace
Ruby | 654 lines | 458 code | 69 blank | 127 comment | 28 complexity | 35b24a23d1dec5038295b92f3a0e1e0a MD5 | raw file
  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 <akr@m17n.org>
 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\)#/ !~ Object.instance_method(:method).bind(self).call(:inspect).inspect
259        q.text self.inspect
260      elsif /\(Kernel\)#/ !~ Object.instance_method(:method).bind(self).call(: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\)#/ =~ Object.instance_method(:method).bind(self).call(: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
492    def test_redefined_method
493      o = ""
494      def o.method
495      end
496      assert_equal(%(""\n), PP.pp(o, ""))
497    end
498  end
499
500  class HasInspect
501    def initialize(a)
502      @a = a
503    end
504
505    def inspect
506      return "<inspect:#{@a.inspect}>"
507    end
508  end
509
510  class HasPrettyPrint
511    def initialize(a)
512      @a = a
513    end
514
515    def pretty_print(q)
516      q.text "<pretty_print:"
517      q.pp @a
518      q.text ">"
519    end
520  end
521
522  class HasBoth
523    def initialize(a)
524      @a = a
525    end
526
527    def inspect
528      return "<inspect:#{@a.inspect}>"
529    end
530
531    def pretty_print(q)
532      q.text "<pretty_print:"
533      q.pp @a
534      q.text ">"
535    end
536  end
537
538  class PrettyPrintInspect < HasPrettyPrint
539    alias inspect pretty_print_inspect
540  end
541
542  class PrettyPrintInspectWithoutPrettyPrint
543    alias inspect pretty_print_inspect
544  end
545
546  class PPInspectTest < Test::Unit::TestCase
547    def test_hasinspect
548      a = HasInspect.new(1)
549      assert_equal("<inspect:1>\n", PP.pp(a, ''))
550    end
551
552    def test_hasprettyprint
553      a = HasPrettyPrint.new(1)
554      assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
555    end
556
557    def test_hasboth
558      a = HasBoth.new(1)
559      assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
560    end
561
562    def test_pretty_print_inspect
563      a = PrettyPrintInspect.new(1)
564      assert_equal("<pretty_print:1>", a.inspect)
565      a = PrettyPrintInspectWithoutPrettyPrint.new
566      assert_raise(RuntimeError) { a.inspect }
567    end
568
569    def test_proc
570      a = proc {1}
571      assert_equal("#{a.inspect}\n", PP.pp(a, ''))
572    end
573
574    def test_to_s_with_iv
575      a = Object.new
576      def a.to_s() "aaa" end
577      a.instance_eval { @a = nil }
578      result = PP.pp(a, '')
579      assert_equal("#{a.inspect}\n", result)
580      assert_match(/\A#<Object.*>\n\z/m, result)
581      a = 1.0
582      a.instance_eval { @a = nil }
583      result = PP.pp(a, '')
584      assert_equal("#{a.inspect}\n", result)
585    end
586    
587    def test_to_s_without_iv
588      a = Object.new
589      def a.to_s() "aaa" end
590      result = PP.pp(a, '')
591      assert_equal("#{a.inspect}\n", result)
592      assert_equal("aaa\n", result)
593    end
594  end
595
596  class PPCycleTest < Test::Unit::TestCase
597    def test_array
598      a = []
599      a << a
600      assert_equal("[[...]]\n", PP.pp(a, ''))
601      assert_equal("#{a.inspect}\n", PP.pp(a, ''))
602    end
603
604    def test_hash
605      a = {}
606      a[0] = a
607      assert_equal("{0=>{...}}\n", PP.pp(a, ''))
608      assert_equal("#{a.inspect}\n", PP.pp(a, ''))
609    end
610
611    S = Struct.new("S", :a, :b)
612    def test_struct
613      a = S.new(1,2)
614      a.b = a
615      assert_equal("#<struct Struct::S a=1, b=#<struct Struct::S:...>>\n", PP.pp(a, ''))
616      assert_equal("#{a.inspect}\n", PP.pp(a, ''))
617    end
618
619    def test_object
620      a = Object.new
621      a.instance_eval {@a = a}
622      assert_equal(a.inspect + "\n", PP.pp(a, ''))
623    end
624
625    def test_anonymous
626      a = Class.new.new
627      assert_equal(a.inspect + "\n", PP.pp(a, ''))
628    end
629
630    def test_withinspect
631      a = []
632      a << HasInspect.new(a)
633      assert_equal("[<inspect:[...]>]\n", PP.pp(a, ''))
634      assert_equal("#{a.inspect}\n", PP.pp(a, ''))
635    end
636
637    def test_share_nil
638      begin
639        PP.sharing_detection = true
640        a = [nil, nil]
641        assert_equal("[nil, nil]\n", PP.pp(a, ''))
642      ensure
643        PP.sharing_detection = false
644      end
645    end
646  end
647
648  class PPSingleLineTest < Test::Unit::TestCase
649    def test_hash
650      assert_equal("{1=>1}", PP.singleline_pp({ 1 => 1}, '')) # [ruby-core:02699]
651      assert_equal("[1#{', 1'*99}]", PP.singleline_pp([1]*100, ''))
652    end
653  end
654end