package Inline::Python; use strict; use Carp; require Inline; require DynaLoader; require Exporter; use vars qw(@ISA $VERSION @EXPORT_OK); @ISA = qw(Inline DynaLoader Exporter); $VERSION = '0.22'; @EXPORT_OK = qw(py_eval py_new_object py_call_method py_call_function py_bind_class py_bind_func py_study_package eval_python ); # Prevent Inline's import from complaining sub import { Inline::Python->export_to_level(1,@_); } sub dl_load_flags { 0x01 } Inline::Python->bootstrap($VERSION); #============================================================================== # Register Python.pm as a valid Inline language #============================================================================== sub register { return { language => 'Python', aliases => ['py', 'python', 'PYTHON'], type => 'interpreted', suffix => 'pydat', }; } #============================================================================== # Validate the Python config options #============================================================================== sub validate { my $o = shift; $o->{ILSM} = {}; $o->{ILSM}{FILTERS} = []; $o->{ILSM}{AUTO_INCLUDE} = {}; $o->{ILSM}{built} = 0; $o->{ILSM}{loaded} = 0; while (@_) { my ($key, $value) = (shift, shift); if ($key eq 'AUTO_INCLUDE') { add_string($o->{ILSM}{AUTO_INCLUDE}, $key, $value, ''); warn "AUTO_INCLUDE has not been implemented yet!\n"; } elsif ($key eq 'FILTERS') { next if $value eq '1' or $value eq '0'; # ignore ENABLE, DISABLE $value = [$value] unless ref($value) eq 'ARRAY'; my %filters; for my $val (@$value) { if (ref($val) eq 'CODE') { $o->add_list($o->{ILSM}, $key, $val, []); } else { eval { require Inline::Filters }; croak "'FILTERS' option requires Inline::Filters to be installed." if $@; %filters = Inline::Filters::get_filters($o->{API}{language}) unless keys %filters; if (defined $filters{$val}) { my $filter = Inline::Filters->new($val, $filters{$val}); $o->add_list($o->{ILSM}, $key, $filter, []); } else { croak "Invalid filter $val specified."; } } } } else { croak "$key is not a valid config option for Python\n"; } next; } } sub usage_validate { return "Invalid value for config option $_[0]"; } sub add_list { my ($ref, $key, $value, $default) = @_; $value = [$value] unless ref $value; croak usage_validate($key) unless ref($value) eq 'ARRAY'; for (@$value) { if (defined $_) { push @{$ref->{$key}}, $_; } else { $ref->{$key} = $default; } } } sub add_string { my ($ref, $key, $value, $default) = @_; $value = [$value] unless ref $value; croak usage_validate($key) unless ref($value) eq 'ARRAY'; for (@$value) { if (defined $_) { $ref->{$key} .= ' ' . $_; } else { $ref->{$key} = $default; } } } sub add_text { my ($ref, $key, $value, $default) = @_; $value = [$value] unless ref $value; croak usage_validate($key) unless ref($value) eq 'ARRAY'; for (@$value) { if (defined $_) { chomp; $ref->{$key} .= $_ . "\n"; } else { $ref->{$key} = $default; } } } #========================================================================== # Print a short information section if PRINT_INFO is enabled. #========================================================================== sub info { my $o = shift; my $info = ""; $o->build unless $o->{ILSM}{built}; my @functions = @{$o->{ILSM}{namespace}{functions}||[]}; $info .= "The following Python functions have been bound to Perl:\n" if @functions; for my $function (sort @functions) { $info .= "\tdef $function()\n"; } my %classes = %{$o->{ILSM}{namespace}{classes}||{}}; $info .= "The following Python classes have been bound to Perl:\n"; for my $class (sort keys %classes) { $info .= "\tclass $class:\n"; for my $method (sort @{$o->{ILSM}{namespace}{classes}{$class}}) { $info .= "\t\tdef $method(...)\n"; } } return $info; } #========================================================================== # Run the code, study the main namespace, and cache the results. #========================================================================== sub build { my $o = shift; return if $o->{ILSM}{cached}; # Filter the code $o->{ILSM}{code} = $o->filter(@{$o->{ILSM}{FILTERS}}); # Run the code py_eval($o->{ILSM}{code}); $o->{ILSM}{evaluated}++; # Study the main namespace my %namespace = py_study_package('__main__'); warn "No functions or classes found!" unless ((length @{$namespace{functions}}) > 0 and (length keys %{$namespace{classes}}) > 0); # Cache the results require Inline::denter; my $namespace = Inline::denter->new ->indent( *namespace => \%namespace, *filtered => $o->{ILSM}{code}, ); $o->mkpath("$o->{API}{install_lib}/auto/$o->{API}{modpname}"); open PYDAT, "> $o->{API}{location}" or croak "Inline::Python couldn't write parse information!"; print PYDAT $namespace; close PYDAT; $o->{ILSM}{namespace} = \%namespace; $o->{ILSM}{cached}++; } #============================================================================== # Load the code, run it, and bind everything to Perl #============================================================================== sub load { my $o = shift; return if $o->{ILSM}{loaded}; # Load the code open PYDAT, $o->{API}{location} or croak "Couldn't open parse info!"; my $pydat = join '', ; close PYDAT; require Inline::denter; my %pydat = Inline::denter->new->undent($pydat); $o->{ILSM}{namespace} = $pydat{namespace}; $o->{ILSM}{code} = $pydat{filtered}; # Run it py_eval($o->{ILSM}{code}) unless $o->{ILSM}{evaluated}; # Bind it all py_bind_func($o->{API}{pkg} . "::$_", '__main__', $_) for (@{ $o->{ILSM}{namespace}{functions} || {} }); py_bind_class($o->{API}{pkg} . "::$_", '__main__', $_, @{$o->{ILSM}{namespace}{classes}{$_}}) for keys %{ $o->{ILSM}{namespace}{classes} || {} }; $o->{ILSM}{loaded}++; } #============================================================================== # Wrap a Python function with a Perl sub which calls it. #============================================================================== sub py_bind_func { my $perlfunc = shift; # What Perl package should the wrapper be in? my $pypkg = shift; # What Python package does it come from? my $function = shift; # What is the name of the Python function? my $bind = <isa("Inline::Python::Object")}; return &py_eval if @_ == 1 || $_[1] =~ /^\d+$/; return &py_call_function if @_ >= 2; croak "Invalid use of eval_python. See 'perldoc Inline::Python'"; } #============================================================================== # A more pleasing name than py_call_function, which is what really happens #============================================================================== sub py_new_object { return &Inline::Python::Object::new; } #============================================================================== # We provide Inline::Python::Object as a base class for Python objects. It # knows how to create, destroy, and call methods on objects. #============================================================================== package Inline::Python::Object; sub new { my $perlpkg = shift; return bless &Inline::Python::py_call_function, $perlpkg; } sub AUTOLOAD { no strict; $AUTOLOAD =~ s|.*::(\w+)|$1|; splice @_, 1, 0, $AUTOLOAD; return &Inline::Python::py_call_method; } 1;