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