Skip to content

Instantly share code, notes, and snippets.

@bazzaar
Last active October 10, 2018 21:04
Show Gist options
  • Save bazzaar/d1436636613f1f1d7e0157df204f98ec to your computer and use it in GitHub Desktop.
Save bazzaar/d1436636613f1f1d7e0157df204f98ec to your computer and use it in GitHub Desktop.
Inline::Python - Testing call method with __builtin__ getattr - fails if called repeatedly on same object / attribute combination
use Inline::Python;
my $py = Inline::Python.new();
$py.run('import matplotlib.pyplot');
class Matplotlib::Plot {
method cm {
class {
# this getattr method, called like so : 'say $plt.cm.getattr($cmap, 'N');'
method getattr($obj, $name) {
$py.call('__builtin__', 'getattr', $obj, $name);
}
method FALLBACK($name, $idx) {
$py.run("matplotlib.pyplot.cm.{$name}($idx)", :eval);
}
}.new();
}
method FALLBACK($name, |c) {
$py.call('matplotlib.pyplot', $name, |c)
}
}
class Matplotlib {
method FALLBACK($name, |c) {
$py.call('matplotlib', $name, |c)
}
}
use v6;
use lib '.';
use Matplotlib_reduced;
my $plt = Matplotlib::Plot.new;
my $cmap = $plt.get_cmap('seismic', 5);
# -- this works
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N');
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N');
# -- this fails on the 3rd call
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N');
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N');
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N');
# -- this works
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name');
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name');
# -- this fails on the 3rd call
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name');
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name');
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name');
# -- this works
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N');
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name');
# -- this fails on the 3rd call
say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name');
say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N');
say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N');
# -- etc....
@0racle
Copy link

0racle commented Oct 10, 2018

Here's one more option... Attributes are not methods, you don't call them... you just ask what they are... sometimes you can assign to them (if Python does not define them as a @property). So in this way, they are kind of like keys of a hash... so you can treat them as such.

    class {
        my $obj  = $py.call( ... );
        method AT-KEY($name) {
            $obj.__getattribute__($name);
        }
        method ASSIGN-KEY($name, $new-value) {
            $obj.__setattr__($name, $new-value)
        }
        method FALLBACK($name, |c) { ... }
    }

Now you could access (and assign to) attributes as if they were a Hash

$pyobject<attributename> = "new";
# calls $pyobject.ASSIGN-KEY('attributename', 'new')...
# which calls $pyobject.__setattr('attributename', 'new')

say $pyobject<attributename>; # new

@0racle
Copy link

0racle commented Oct 10, 2018

Upon further experimentation, it seems like problems happen as soon as you start doing calls to methods on __builtin__. If I avoid those methods, I don't seem to hit the same problem.

For example, in my FALLBACK in the earlier comment, if I don't check __builtin__.hasattr then it works, but of course, Python will throw an exception if the attribute doesn't exists. You can put a try in front of the call to __getattribute__ and it will return an undefined value (instead of throwing) if the attribute doesn't exists.

I'm not yet sure how to put all these ideas together in a cohesive whole, but play around with them and see what you come up with.

@moritz
Copy link

moritz commented Oct 10, 2018

btw python's getattr supports a third argument which is a default value in case the attribute doesn't exist.

If you write a wrapper, maybe the simplest thing to do is to create a sentinel value Any.new as the default, and if that is returned, interpret it as not found.

@bazzaar
Copy link
Author

bazzaar commented Oct 10, 2018

More thanks 0racle, and Moritz, I definitely got way more out of this than I put in :-) I hope this thread has somehow kickstarted a future new article on your Perl6/Matplotlib blog 0racle. I agree about not bugging the Inline::Python maintainer over this when there is a work around.

It all seems so straightforward when it is explained, like you have both done in your comments, I'm not sure after a month of researching I'd have come to the same understanding :-) It's given me a whole ton of concepts and reasoning to investigate. I'll carry on with my efforts on this, and try to add some results to this gist when I have them. Thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment