0

I stumbled into this while struggling with this issue - but I guess this is more of a Ruby language-level inquiry.

I have a method that accepts four params:

def render_node_to_output(node, output, context, skip_output = false)

(For those who are curious, the method I'm talking about is Liquid::BlockBody::render_node_to_output of liquid:4.0.3 (on Linux, line 102 on gems/liquid-4.0.3/lib/liquid/block_body.rb).)

Right at the start of the method, I inspect the first three variables as:

p node
p output
p context
print("\n\n")

This method is being invoked repeatedly by different components, and at some points I get the error:

/root/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/jekyll-3.8.5/lib/jekyll/filters.rb:292:in `inspect': wrong number of arguments (given 0, expected 1) (ArgumentError)
    from /root/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:105:in `inspect'
    from /root/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:105:in `p'
    from /root/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:105:in `render_node_to_output'

Apparently the context variable is going into some "non-existent" (?) state at some point.

I tried adding print(context.class) and print(context) right before p context, and they do display the same, acceptable values for both success and failure scenarios: Liquid::Context and #<Liquid::Context:0x00007fffd43d9948>.

My queries:

  • why does inspect say it didn't receive an argument when I clearly passed it one? More importantly, why does this happen only for some values of context? AFAIK even if context were nil, inspect should not fail (ruby -e "x = nil; p x" produces nil). I couldn't find any references as to why or when inspect could behave in this manner.
  • what is the correct way to skip such "bad" states of context so that I can at least continue inspecting "good" values without breaking the program at the first "bad" state - ideally without using a try-catch strategy?

(I am pretty new to Ruby, so apologies if I missed/misused any jargon.)

Janaka Bandara
  • 1,024
  • 1
  • 12
  • 27

1 Answers1

1

The _argument error says: given 0, expected 1 for inspect. This means that the inspect being invoked here, expects one argument. But inspect is always supposed to be invoked without arguments.

From this I conclude that the method inspect has been redefined for context.class.

UPDATE:

Now, based on the comment of the OP to my answer, I suggest the following modification the failing program:

First, we verify that the exception really comes from that invocation of inspect that we suspect; though it seems to be obvious, the devil nevers sleeps and we want to go sure. If it does, we have a closer look at the properties of the offending object:

We replace p context by

begin
  # Doing an explicit `inspect` to be in control of what is going on
  ic  = context.inspect
  puts ic
rescue ArgumentError => e
  puts "Exception!"
  puts e
  puts "context is a #{context.class}"
  puts "inspect expects: #{context.class.method(:inspect).parameters}"
end

If it is really the case, as the OP claims, that this code displays Exception!, but still shows an empty parameter array, I would report this exact example to the Ruby bug tracker.

user1934428
  • 19,864
  • 7
  • 42
  • 87
  • Thanks @user1934428! I failed to mention that I hit the **same class** (`Liquid::Context`) in all success cases **as well as** the failure case (just edited the question to reflect that). So I'm not sure if this could explain the behavior (succeeding for many `Liquid::Context` instances and failing for one or a few) - unless it is possible to override `inspect` on a per-object (instance) basis? – Janaka Bandara Mar 30 '20 at 07:27
  • 1
    You can override everything, and add individual methods to, a single object. – user1934428 Mar 31 '20 at 06:53
  • 1
    @JanakaBandara : For debugging, you could do a `m=context.method(:inspect)`. This stores in `m` an object of class `Method`, which you can examine further. For instance, `m.parameters` yields an array with information about the parameters. With the `:inspect` method, this should be an empty array. – user1934428 Mar 31 '20 at 07:01
  • Thanks! I tried adding `print(context.method(:inspect).parameters)` before `p context` but it always prints an empty array (for successes as well as the failure case)... not sure if I'm missing something? – Janaka Bandara Apr 01 '20 at 03:53
  • 1
    @JanakaBandara : Now this gets interesting. I'll amend my answer with respect to your findings. – user1934428 Apr 01 '20 at 06:00
  • Thanks @user1934428 - been having some strange issues (the error is not being captured by `rescue` - even when I change `ArgumentError` to `Error`)... will keep on digging and post back my findings – Janaka Bandara Apr 07 '20 at 01:32
  • 1
    `ArgumentError` is a child class of `StandardError` and can be caught, as you can test by yourself in irb by doing `begin; 'x'.size(3); rescue ArgumentError; puts 'Exception'; end`. If there is no error captured, the error exception does not occur in that spot you are looking at. In the code I suggested in my answer, I have included a `puts ic`. Can you see the output of this? – user1934428 Apr 07 '20 at 08:00
  • Finally got it! They seem to have their own `ArgumentError` that doesn't derive from `StandardError` - so only `rescue Exception` worked. Yet, the result is strange: `Exception! | wrong number of arguments (given 0, expected 1) | context is a Liquid::Context | inspect expects: []` ... Off to Ruby bug tracker, I presume? – Janaka Bandara Apr 09 '20 at 01:55
  • 1
    I don't think that any sane implementation would at runtime remove a standard class from the system, and insert instead their own implementation outside of the standard inheritance hierarchy. Technically, this is possible, but there is nothing Ruby can do about is, and I don't think any sane programmer would do it. Well, a sane programmer wouldn't redefine the signature of `inspect`either. When catching the exception, what does `puts e.class` say? I would send a bug report to the maintainers of `Liquid::Context`. – user1934428 Apr 09 '20 at 08:14
  • Sorry about the delay! `puts e.class` → `ArgumentError`. Some more info: the class I'm in, has a `module Liquid` declaration at the top; and there's another `errors.rb` file with the same module declaration, containing a line `ArgumentError = Class.new(Error)`. Now, when I say `rescue ::ArgumentError` in the original code, it catches the error (but not if I say `ArgumentError` or, strangely, even `Liquid::ArgumentError`); I suppose this is because the error being thrown is actually a `Liquid::ArgumentError`? Thanks for your continued support - especially given my lack of Ruby knowledge! – Janaka Bandara Apr 18 '20 at 05:32
  • Of course, if `ArgumentError` is inside `module Liquid; ....; end`, the type is `Liquid::ArgumentError`, but then, the `puts e.class` should print `Liquid::ArgumentError`, not just `ArgumentError`, and it still does not explain, why `inspect` has been redefined. Can you find a `def inspect` or `def_method(:inspect)` somewhere in the code for `Liquid`? At this point, I would try to extract from the files involved just that part of code which is in minimum necessary to reproduce the error. This is tedious, but I don't see any alternative to track down the problem. – user1934428 Apr 19 '20 at 09:26
  • 1
    Acutally, I would do a `grep -Er 'def.+inspect'` on the whole source code involved, to be sure that nothing is missed. – user1934428 Apr 19 '20 at 09:27