104
104
from __future__ import absolute_import , division , print_function
105
105
106
106
import six
107
+ from six .moves .urllib .request import urlopen
108
+ from six .moves import reload_module as reload
107
109
108
110
import sys
109
111
if sys .version_info < (3 , 5 ): # noqa: E402
118
120
""" )
119
121
120
122
import atexit
121
- from collections import MutableMapping
123
+ from collections import MutableMapping , namedtuple
122
124
import contextlib
123
- import distutils .version
124
- import distutils .sysconfig
125
+ from distutils .version import LooseVersion
125
126
import functools
126
127
import io
127
128
import inspect
140
141
# definitions, so it is safe to import from it here.
141
142
from . import cbook
142
143
from matplotlib .cbook import (
143
- _backports , mplDeprecation , dedent , get_label , sanitize_sequence )
144
+ mplDeprecation , dedent , get_label , sanitize_sequence )
144
145
from matplotlib .compat import subprocess
145
146
from matplotlib .rcsetup import defaultParams , validate_backend , cycler
146
147
147
148
import numpy
148
- from six .moves .urllib .request import urlopen
149
- from six .moves import reload_module as reload
150
149
151
150
# Get the version from the _version.py versioneer file. For a git checkout,
152
151
# this is computed based on the number of commits since the last tag.
@@ -191,9 +190,7 @@ def compare_versions(a, b):
191
190
a = a .decode ('ascii' )
192
191
if isinstance (b , bytes ):
193
192
b = b .decode ('ascii' )
194
- a = distutils .version .LooseVersion (a )
195
- b = distutils .version .LooseVersion (b )
196
- return a >= b
193
+ return LooseVersion (a ) >= LooseVersion (b )
197
194
else :
198
195
return False
199
196
@@ -421,89 +418,123 @@ def wrapper(*args, **kwargs):
421
418
return wrapper
422
419
423
420
421
+ _ExecInfo = namedtuple ("_ExecInfo" , "executable version" )
422
+
423
+
424
+ @functools .lru_cache ()
425
+ def get_executable_info (name ):
426
+ """Get the version of some executables that Matplotlib depends on.
427
+
428
+ .. warning:
429
+ The list of executables that this function supports is set according to
430
+ Matplotlib's internal needs, and may change without notice.
431
+
432
+ Parameters
433
+ ----------
434
+ name : str
435
+ The executable to query. The following values are currently supported:
436
+ "dvipng", "gs", "inkscape", "pdftops", "tex". This list is subject to
437
+ change without notice.
438
+
439
+ Returns
440
+ -------
441
+ If the executable is found, a namedtuple with fields ``executable`` (`str`)
442
+ and ``version`` (`distutils.version.LooseVersion`, or ``None`` if the
443
+ version cannot be determined); ``None`` if the executable is not found.
444
+ """
445
+
446
+ def impl (args , regex , min_ver = None ):
447
+ # Execute the subprocess specified by args; capture stdout and stderr.
448
+ # Search for a regex match in the output; if the match succeeds, use
449
+ # the *first group* of the match as the version.
450
+ # If min_ver is not None, emit a warning if the version is less than
451
+ # min_ver.
452
+ try :
453
+ proc = subprocess .Popen (
454
+ args , stdout = subprocess .PIPE , stderr = subprocess .STDOUT ,
455
+ universal_newlines = True )
456
+ proc .wait ()
457
+ except OSError :
458
+ return None
459
+ match = re .search (regex , proc .stdout .read ())
460
+ if match :
461
+ version = LooseVersion (match .group (1 ))
462
+ if min_ver is not None and version < min_ver :
463
+ warnings .warn ("You have {} version {} but the minimum version "
464
+ "supported by Matplotlib is {}."
465
+ .format (args [0 ], version , min_ver ))
466
+ return None
467
+ return _ExecInfo (args [0 ], version )
468
+ else :
469
+ return None
470
+
471
+ if name == "dvipng" :
472
+ info = impl (["dvipng" , "-version" ], "(?m)^dvipng .* (.+)" , "1.6" )
473
+ elif name == "gs" :
474
+ execs = (["gswin32c" , "gswin64c" , "mgs" , "gs" ] # "mgs" for miktex.
475
+ if sys .platform == "win32" else
476
+ ["gs" ])
477
+ info = next (filter (None , (impl ([e , "--version" ], "(.*)" , "8.60" )
478
+ for e in execs )),
479
+ None )
480
+ elif name == "inkscape" :
481
+ info = impl (["inkscape" , "-V" ], "^Inkscape ([^ ]*)" )
482
+ elif name == "pdftops" :
483
+ info = impl (["pdftops" , "-v" ], "^pdftops version (.*)" )
484
+ if info and not ("3.0" <= info .version
485
+ # poppler version numbers.
486
+ or "0.9" <= info .version <= "1.0" ):
487
+ warnings .warn (
488
+ "You have pdftops version {} but the minimum version "
489
+ "supported by Matplotlib is 3.0." .format (info .version ))
490
+ return None
491
+ elif name == "tex" :
492
+ info = (_ExecInfo ("tex" , None ) if shutil .which ("tex" ) is not None
493
+ else None )
494
+ else :
495
+ raise ValueError ("Unknown executable: {!r}" .format (name ))
496
+ return info
497
+
498
+
499
+ def get_all_executable_infos ():
500
+ """Query all executables that Matplotlib may need.
501
+
502
+ .. warning:
503
+ The list of executables that this function queries is set according to
504
+ Matplotlib's internal needs, and may change without notice.
505
+
506
+ Returns
507
+ -------
508
+ A mapping of the required executable to its corresponding information,
509
+ as returned by `get_executable_info`. The keys in the mapping are subject
510
+ to change without notice.
511
+ """
512
+ return {name : get_executable_info (name )
513
+ for name in ["dvipng" , "gs" , "inkscape" , "pdftops" , "tex" ]}
514
+
515
+
516
+ @cbook .deprecated ("3.0" )
424
517
def checkdep_dvipng ():
425
- try :
426
- s = subprocess .Popen ([str ('dvipng' ), '-version' ],
427
- stdout = subprocess .PIPE ,
428
- stderr = subprocess .PIPE )
429
- stdout , stderr = s .communicate ()
430
- line = stdout .decode ('ascii' ).split ('\n ' )[1 ]
431
- v = line .split ()[- 1 ]
432
- return v
433
- except (IndexError , ValueError , OSError ):
434
- return None
518
+ return str (get_executable_info ("dvipng" ).version )
435
519
436
520
437
521
def checkdep_ghostscript ():
438
- if checkdep_ghostscript .executable is None :
439
- if sys .platform == 'win32' :
440
- # mgs is the name in miktex
441
- gs_execs = ['gswin32c' , 'gswin64c' , 'mgs' , 'gs' ]
442
- else :
443
- gs_execs = ['gs' ]
444
- for gs_exec in gs_execs :
445
- try :
446
- s = subprocess .Popen (
447
- [str (gs_exec ), '--version' ], stdout = subprocess .PIPE ,
448
- stderr = subprocess .PIPE )
449
- stdout , stderr = s .communicate ()
450
- if s .returncode == 0 :
451
- v = stdout [:- 1 ].decode ('ascii' )
452
- checkdep_ghostscript .executable = gs_exec
453
- checkdep_ghostscript .version = v
454
- except (IndexError , ValueError , OSError ):
455
- pass
522
+ info = get_executable_info ("gs" )
523
+ checkdep_ghostscript .executable = info .executable
524
+ checkdep_ghostscript .version = str (info .version )
456
525
return checkdep_ghostscript .executable , checkdep_ghostscript .version
457
526
checkdep_ghostscript .executable = None
458
527
checkdep_ghostscript .version = None
459
528
460
529
461
- # Deprecated, as it is unneeded and some distributions (e.g. MiKTeX 2.9.6350)
462
- # do not actually report the TeX version.
463
- @cbook .deprecated ("2.1" )
464
- def checkdep_tex ():
465
- try :
466
- s = subprocess .Popen ([str ('tex' ), '-version' ], stdout = subprocess .PIPE ,
467
- stderr = subprocess .PIPE )
468
- stdout , stderr = s .communicate ()
469
- line = stdout .decode ('ascii' ).split ('\n ' )[0 ]
470
- pattern = r'3\.1\d+'
471
- match = re .search (pattern , line )
472
- v = match .group (0 )
473
- return v
474
- except (IndexError , ValueError , AttributeError , OSError ):
475
- return None
476
-
477
-
530
+ @cbook .deprecated ("3.0" )
478
531
def checkdep_pdftops ():
479
- try :
480
- s = subprocess .Popen ([str ('pdftops' ), '-v' ], stdout = subprocess .PIPE ,
481
- stderr = subprocess .PIPE )
482
- stdout , stderr = s .communicate ()
483
- lines = stderr .decode ('ascii' ).split ('\n ' )
484
- for line in lines :
485
- if 'version' in line :
486
- v = line .split ()[- 1 ]
487
- return v
488
- except (IndexError , ValueError , UnboundLocalError , OSError ):
489
- return None
532
+ return str (get_executable_info ("pdftops" ).version )
490
533
491
534
535
+ @cbook .deprecated ("3.0" )
492
536
def checkdep_inkscape ():
493
- if checkdep_inkscape .version is None :
494
- try :
495
- s = subprocess .Popen ([str ('inkscape' ), '-V' ],
496
- stdout = subprocess .PIPE ,
497
- stderr = subprocess .PIPE )
498
- stdout , stderr = s .communicate ()
499
- lines = stdout .decode ('ascii' ).split ('\n ' )
500
- for line in lines :
501
- if 'Inkscape' in line :
502
- v = line .split ()[1 ]
503
- break
504
- checkdep_inkscape .version = v
505
- except (IndexError , ValueError , UnboundLocalError , OSError ):
506
- pass
537
+ checkdep_inkscape .version = str (get_executable_info ("inkscape" ).version )
507
538
return checkdep_inkscape .version
508
539
checkdep_inkscape .version = None
509
540
@@ -528,65 +559,31 @@ def checkdep_xmllint():
528
559
def checkdep_ps_distiller (s ):
529
560
if not s :
530
561
return False
531
-
532
- flag = True
533
- gs_req = '8.60'
534
- gs_exec , gs_v = checkdep_ghostscript ()
535
- if not compare_versions (gs_v , gs_req ):
536
- flag = False
537
- warnings .warn (('matplotlibrc ps.usedistiller option can not be used '
538
- 'unless ghostscript-%s or later is installed on your '
539
- 'system' ) % gs_req )
540
-
541
- if s == 'xpdf' :
542
- pdftops_req = '3.0'
543
- pdftops_req_alt = '0.9' # poppler version numbers, ugh
544
- pdftops_v = checkdep_pdftops ()
545
- if compare_versions (pdftops_v , pdftops_req ):
546
- pass
547
- elif (compare_versions (pdftops_v , pdftops_req_alt ) and not
548
- compare_versions (pdftops_v , '1.0' )):
549
- pass
550
- else :
551
- flag = False
552
- warnings .warn (('matplotlibrc ps.usedistiller can not be set to '
553
- 'xpdf unless xpdf-%s or later is installed on '
554
- 'your system' ) % pdftops_req )
555
-
556
- if flag :
557
- return s
558
- else :
562
+ if not get_executable_info ("gs" ):
563
+ warnings .warn (
564
+ "Setting matplotlibrc ps.usedistiller requires ghostscript." )
559
565
return False
566
+ if s == "xpdf" and not get_executable_info ("pdftops" ):
567
+ warnings .warn (
568
+ "setting matplotlibrc ps.usedistiller to 'xpdf' requires xpdf." )
569
+ return False
570
+ return s
560
571
561
572
562
573
def checkdep_usetex (s ):
563
574
if not s :
564
575
return False
565
-
566
- gs_req = '8.60'
567
- dvipng_req = '1.6'
568
- flag = True
569
-
570
- if shutil .which ("tex" ) is None :
571
- flag = False
572
- warnings .warn ('matplotlibrc text.usetex option can not be used unless '
573
- 'TeX is installed on your system' )
574
-
575
- dvipng_v = checkdep_dvipng ()
576
- if not compare_versions (dvipng_v , dvipng_req ):
577
- flag = False
578
- warnings .warn ('matplotlibrc text.usetex can not be used with *Agg '
579
- 'backend unless dvipng-%s or later is installed on '
580
- 'your system' % dvipng_req )
581
-
582
- gs_exec , gs_v = checkdep_ghostscript ()
583
- if not compare_versions (gs_v , gs_req ):
584
- flag = False
585
- warnings .warn ('matplotlibrc text.usetex can not be used unless '
586
- 'ghostscript-%s or later is installed on your system'
587
- % gs_req )
588
-
589
- return flag
576
+ if not get_executable_info ("tex" ):
577
+ warnings .warn ("Setting matplotlibrc text.usetex requires TeX." )
578
+ return False
579
+ if not get_executable_info ("dvipng" ):
580
+ warnings .warn ("Setting matplotlibrc text.usetex requires dvipng." )
581
+ return False
582
+ if not get_executable_info ("gs" ):
583
+ warnings .warn (
584
+ "Setting matplotlibrc text.usetex requires ghostscript." )
585
+ return False
586
+ return True
590
587
591
588
592
589
def _get_home ():
0 commit comments