File Coverage

blib/lib/Doit/Pip.pm
Criterion Covered Total %
statement 20 51 39.2
branch 2 16 12.5
condition 0 3 0.0
subroutine 8 10 80.0
pod 4 6 66.6
total 34 86 39.5


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             #
4             # Author: Slaven Rezic
5             #
6             # Copyright (C) 2020,2022,2024 Slaven Rezic. All rights reserved.
7             # This package is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl itself.
9             #
10             # Mail: slaven@rezic.de
11             # WWW: https://github.com/eserte/Doit
12             #
13              
14             package Doit::Pip; # Convention: all commands here should be prefixed with 'pip_'
15              
16 1     1   5 use strict;
  1         2  
  1         28  
17 1     1   3 use warnings;
  1         1  
  1         43  
18              
19             our $VERSION = '0.013';
20              
21 1     1   3 use Doit::Log;
  1         1  
  1         499  
22              
23 1     1 0 8 sub new { bless {}, shift }
24 1     1 0 3 sub functions { qw(pip_install_packages pip_missing_packages can_pip pip_is_functional) }
25              
26             sub can_pip {
27 1     1 1 1 my($self) = @_;
28 1 50       3 $self->which('pip3') ? 1 : 0;
29             }
30              
31             sub pip_is_functional {
32 1     1 1 2 my($self) = @_;
33 1 50       2 if (!$self->can_pip) {
34 1         4 info "pip3 is not installed";
35 1         81 return 0;
36             }
37 0           my $help_text = eval { $self->info_qx({quiet=>1}, 'pip3', '--help') };
  0            
38 0 0         if ($@) {
39 0           warning "$@";
40 0           return 0;
41             }
42 0 0         if (!$help_text) {
43 0           warning "pip3 --help did not return anything";
44 0           return 0;
45             }
46 0 0 0       if ($help_text !~ /pip/ || $help_text =~ /install.*package/i) {
47 0           warning "pip3 help text looks suspicious";
48 0           return 0;
49             }
50 0           1;
51             }
52              
53             sub pip_install_packages {
54 0     0 1   my($self, @packages) = @_;
55 0           my @missing_packages = $self->pip_missing_packages(@packages);
56 0 0         if (@missing_packages) {
57 0           $self->system('pip3', 'install', @missing_packages);
58             }
59 0           @missing_packages;
60             }
61              
62             sub pip_missing_packages {
63 0     0 1   my($self, @packages) = @_;
64 0           my @missing_packages;
65 0           if (0) { # XXX this is only possible with newer pip versions --- how new?
66             my $stderr;
67             eval {
68             $self->info_open3({
69             quiet => 1,
70             errref => \$stderr,
71             }, 'pip3', 'show', '-q', '--no-color', @packages); # note: --no-color is only available in newer pip versions
72             };
73             if ($stderr) {
74             if ($stderr =~ m{\QWARNING: Package(s) not found:\E\s+(.+)}) {
75             @missing_packages = split /,\s+/, $1;
76             } else {
77             error "Unable to parse stderr output of 'pip3 show': '$stderr'";
78             }
79             }
80             } else {
81 0           if (0) { # XXX this solution would be straightforward --- unfortunately some pip3 versions do not exit with a non-zero status for missing packages
82             for my $package (@packages) {
83             if (!eval { $self->info_open3({quiet=>1}, 'pip3', 'show', '-q', $package); 1 }) {
84             push @missing_packages, $package;
85             }
86             }
87             } else {
88 0           my %seen_package;
89 1     1   6 for my $line (do { no warnings "uninitialized"; split /\n/, eval { $self->info_open3({quiet=>1}, 'pip3', 'show', @packages) } }) {
  1         1  
  1         179  
  0            
  0            
  0            
90 0 0         if ($line =~ /^Name:\s+(\S+)/) {
91 0           $seen_package{$1}++;
92             }
93             }
94 0           for my $package (@packages) {
95 0 0         if (!$seen_package{$package}) {
96 0           push @missing_packages, $package;
97             }
98             }
99             }
100             }
101 0           @missing_packages;
102             }
103              
104             1;
105              
106             __END__