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
- from collections import MutableMapping
109
+ from collections import MutableMapping , namedtuple
108
110
import contextlib
109
- import distutils .version
110
- import distutils .sysconfig
111
+ from distutils .version import LooseVersion
111
112
import functools
112
113
import io
113
114
import inspect
119
120
import tempfile
120
121
import warnings
121
122
123
+ try :
124
+ from functools import lru_cache
125
+ except ImportError :
126
+ from backports .functools_lru_cache import lru_cache
127
+
122
128
# cbook must import matplotlib only within function
123
129
# definitions, so it is safe to import from it here.
124
130
from . import cbook
128
134
from matplotlib .rcsetup import defaultParams , validate_backend , cycler
129
135
130
136
import numpy
131
- from six .moves .urllib .request import urlopen
132
- from six .moves import reload_module as reload
133
137
134
138
# Get the version from the _version.py versioneer file. For a git checkout,
135
139
# this is computed based on the number of commits since the last tag.
@@ -169,9 +173,7 @@ def compare_versions(a, b):
169
173
a = a .decode ('ascii' )
170
174
if isinstance (b , bytes ):
171
175
b = b .decode ('ascii' )
172
- a = distutils .version .LooseVersion (a )
173
- b = distutils .version .LooseVersion (b )
174
- return a >= b
176
+ return LooseVersion (a ) >= LooseVersion (b )
175
177
else :
176
178
return False
177
179
@@ -314,89 +316,125 @@ def ge(self, level):
314
316
verbose = Verbose ()
315
317
316
318
319
+ _ExecInfo = namedtuple ("_ExecInfo" , "executable version" )
320
+
321
+
322
+ @lru_cache ()
323
+ def get_executable_info (name ):
324
+ """Get the version of some executables that Matplotlib depends on.
325
+
326
+ .. warning:
327
+ The list of executables that this function supports is set according to
328
+ Matplotlib's internal needs, and may change without notice.
329
+
330
+ Parameters
331
+ ----------
332
+ name : str
333
+ The executable to query. The following values are currently supported:
334
+ "dvipng", "gs", "inkscape", "pdftops", "tex". This list is subject to
335
+ change without notice.
336
+
337
+ Returns
338
+ -------
339
+ If the executable is found, a namedtuple with fields ``executable`` (`str`)
340
+ and ``version`` (`distutils.version.LooseVersion`, or ``None`` if the
341
+ version cannot be determined); ``None`` if the executable is not found.
342
+ """
343
+
344
+ def impl (args , regex , min_ver = None ):
345
+ # Execute the subprocess specified by args; capture stdout and stderr.
346
+ # Search for a regex match in the output; if the match succeeds, use
347
+ # the *first group* of the match as the version.
348
+ # If min_ver is not None, emit a warning if the version is less than
349
+ # min_ver.
350
+ try :
351
+ proc = subprocess .Popen (
352
+ [str (arg ) for arg in args ], # str(): Py2 compat.
353
+ stdout = subprocess .PIPE , stderr = subprocess .STDOUT ,
354
+ universal_newlines = True )
355
+ proc .wait ()
356
+ except OSError :
357
+ return None
358
+ match = re .search (regex , proc .stdout .read ())
359
+ if match :
360
+ version = LooseVersion (match .group (1 ))
361
+ if min_ver is not None and version < str (min_ver ):
362
+ warnings .warn ("You have {} version {} but the minimum version "
363
+ "supported by Matplotlib is {}."
364
+ .format (args [0 ], version , min_ver ))
365
+ return None
366
+ return _ExecInfo (str (args [0 ]), version ) # str(): Py2 compat.
367
+ else :
368
+ return None
369
+
370
+ if name == "dvipng" :
371
+ info = impl (["dvipng" , "-version" ], "(?m)^dvipng .* (.+)" , "1.6" )
372
+ elif name == "gs" :
373
+ execs = (["gswin32c" , "gswin64c" , "mgs" , "gs" ] # "mgs" for miktex.
374
+ if sys .platform == "win32" else
375
+ ["gs" ])
376
+ info = next (filter (None , (impl ([e , "--version" ], "(.*)" , "8.60" )
377
+ for e in execs )),
378
+ None )
379
+ elif name == "inkscape" :
380
+ info = impl (["inkscape" , "-V" ], "^Inkscape ([^ ]*)" )
381
+ elif name == "pdftops" :
382
+ info = impl (["pdftops" , "-v" ], "^pdftops version (.*)" )
383
+ if info and not (str ("3.0" ) <= info .version
384
+ # poppler version numbers.
385
+ or str ("0.9" ) <= info .version <= str ("1.0" )):
386
+ warnings .warn (
387
+ "You have pdftops version {} but the minimum version "
388
+ "supported by Matplotlib is 3.0." .format (info .version ))
389
+ return None
390
+ elif name == "tex" :
391
+ info = (_ExecInfo (str ("tex" ), None ) # str(): Py2 compat.
392
+ if _backports .which ("tex" ) is not None
393
+ else None )
394
+ else :
395
+ raise ValueError ("Unknown executable: {!r}" .format (name ))
396
+ return info
397
+
398
+
399
+ def get_all_executable_infos ():
400
+ """Query all executables that Matplotlib may need.
401
+
402
+ .. warning:
403
+ The list of executables that this function queries is set according to
404
+ Matplotlib's internal needs, and may change without notice.
405
+
406
+ Returns
407
+ -------
408
+ A mapping of the required executable to its corresponding information,
409
+ as returned by `get_executable_info`. The keys in the mapping are subject
410
+ to change without notice.
411
+ """
412
+ return {name : get_executable_info (name )
413
+ for name in ["dvipng" , "gs" , "inkscape" , "pdftops" , "tex" ]}
414
+
415
+
416
+ @cbook .deprecated ("2.2" )
317
417
def checkdep_dvipng ():
318
- try :
319
- s = subprocess .Popen ([str ('dvipng' ), '-version' ],
320
- stdout = subprocess .PIPE ,
321
- stderr = subprocess .PIPE )
322
- stdout , stderr = s .communicate ()
323
- line = stdout .decode ('ascii' ).split ('\n ' )[1 ]
324
- v = line .split ()[- 1 ]
325
- return v
326
- except (IndexError , ValueError , OSError ):
327
- return None
418
+ return str (get_executable_info ("dvipng" ).version )
328
419
329
420
330
421
def checkdep_ghostscript ():
331
- if checkdep_ghostscript .executable is None :
332
- if sys .platform == 'win32' :
333
- # mgs is the name in miktex
334
- gs_execs = ['gswin32c' , 'gswin64c' , 'mgs' , 'gs' ]
335
- else :
336
- gs_execs = ['gs' ]
337
- for gs_exec in gs_execs :
338
- try :
339
- s = subprocess .Popen (
340
- [str (gs_exec ), '--version' ], stdout = subprocess .PIPE ,
341
- stderr = subprocess .PIPE )
342
- stdout , stderr = s .communicate ()
343
- if s .returncode == 0 :
344
- v = stdout [:- 1 ].decode ('ascii' )
345
- checkdep_ghostscript .executable = gs_exec
346
- checkdep_ghostscript .version = v
347
- except (IndexError , ValueError , OSError ):
348
- pass
422
+ info = get_executable_info ("gs" )
423
+ checkdep_ghostscript .executable = info .executable
424
+ checkdep_ghostscript .version = str (info .version )
349
425
return checkdep_ghostscript .executable , checkdep_ghostscript .version
350
426
checkdep_ghostscript .executable = None
351
427
checkdep_ghostscript .version = None
352
428
353
429
354
- # Deprecated, as it is unneeded and some distributions (e.g. MiKTeX 2.9.6350)
355
- # do not actually report the TeX version.
356
- @cbook .deprecated ("2.1" )
357
- def checkdep_tex ():
358
- try :
359
- s = subprocess .Popen ([str ('tex' ), '-version' ], stdout = subprocess .PIPE ,
360
- stderr = subprocess .PIPE )
361
- stdout , stderr = s .communicate ()
362
- line = stdout .decode ('ascii' ).split ('\n ' )[0 ]
363
- pattern = r'3\.1\d+'
364
- match = re .search (pattern , line )
365
- v = match .group (0 )
366
- return v
367
- except (IndexError , ValueError , AttributeError , OSError ):
368
- return None
369
-
370
-
430
+ @cbook .deprecated ("2.2" )
371
431
def checkdep_pdftops ():
372
- try :
373
- s = subprocess .Popen ([str ('pdftops' ), '-v' ], stdout = subprocess .PIPE ,
374
- stderr = subprocess .PIPE )
375
- stdout , stderr = s .communicate ()
376
- lines = stderr .decode ('ascii' ).split ('\n ' )
377
- for line in lines :
378
- if 'version' in line :
379
- v = line .split ()[- 1 ]
380
- return v
381
- except (IndexError , ValueError , UnboundLocalError , OSError ):
382
- return None
432
+ return str (get_executable_info ("pdftops" ).version )
383
433
384
434
435
+ @cbook .deprecated ("2.2" )
385
436
def checkdep_inkscape ():
386
- if checkdep_inkscape .version is None :
387
- try :
388
- s = subprocess .Popen ([str ('inkscape' ), '-V' ],
389
- stdout = subprocess .PIPE ,
390
- stderr = subprocess .PIPE )
391
- stdout , stderr = s .communicate ()
392
- lines = stdout .decode ('ascii' ).split ('\n ' )
393
- for line in lines :
394
- if 'Inkscape' in line :
395
- v = line .split ()[1 ]
396
- break
397
- checkdep_inkscape .version = v
398
- except (IndexError , ValueError , UnboundLocalError , OSError ):
399
- pass
437
+ checkdep_inkscape .version = str (get_executable_info ("inkscape" ).version )
400
438
return checkdep_inkscape .version
401
439
checkdep_inkscape .version = None
402
440
@@ -421,65 +459,31 @@ def checkdep_xmllint():
421
459
def checkdep_ps_distiller (s ):
422
460
if not s :
423
461
return False
424
-
425
- flag = True
426
- gs_req = '8.60'
427
- gs_exec , gs_v = checkdep_ghostscript ()
428
- if not compare_versions (gs_v , gs_req ):
429
- flag = False
430
- warnings .warn (('matplotlibrc ps.usedistiller option can not be used '
431
- 'unless ghostscript-%s or later is installed on your '
432
- 'system' ) % gs_req )
433
-
434
- if s == 'xpdf' :
435
- pdftops_req = '3.0'
436
- pdftops_req_alt = '0.9' # poppler version numbers, ugh
437
- pdftops_v = checkdep_pdftops ()
438
- if compare_versions (pdftops_v , pdftops_req ):
439
- pass
440
- elif (compare_versions (pdftops_v , pdftops_req_alt ) and not
441
- compare_versions (pdftops_v , '1.0' )):
442
- pass
443
- else :
444
- flag = False
445
- warnings .warn (('matplotlibrc ps.usedistiller can not be set to '
446
- 'xpdf unless xpdf-%s or later is installed on '
447
- 'your system' ) % pdftops_req )
448
-
449
- if flag :
450
- return s
451
- else :
462
+ if not get_executable_info ("gs" ):
463
+ warnings .warn (
464
+ "Setting matplotlibrc ps.usedistiller requires ghostscript." )
452
465
return False
466
+ if s == "xpdf" and not get_executable_info ("pdftops" ):
467
+ warnings .warn (
468
+ "setting matplotlibrc ps.usedistiller to 'xpdf' requires xpdf." )
469
+ return False
470
+ return s
453
471
454
472
455
473
def checkdep_usetex (s ):
456
474
if not s :
457
475
return False
458
-
459
- gs_req = '8.60'
460
- dvipng_req = '1.6'
461
- flag = True
462
-
463
- if _backports .which ("tex" ) is None :
464
- flag = False
465
- warnings .warn ('matplotlibrc text.usetex option can not be used unless '
466
- 'TeX is installed on your system' )
467
-
468
- dvipng_v = checkdep_dvipng ()
469
- if not compare_versions (dvipng_v , dvipng_req ):
470
- flag = False
471
- warnings .warn ('matplotlibrc text.usetex can not be used with *Agg '
472
- 'backend unless dvipng-%s or later is installed on '
473
- 'your system' % dvipng_req )
474
-
475
- gs_exec , gs_v = checkdep_ghostscript ()
476
- if not compare_versions (gs_v , gs_req ):
477
- flag = False
478
- warnings .warn ('matplotlibrc text.usetex can not be used unless '
479
- 'ghostscript-%s or later is installed on your system'
480
- % gs_req )
481
-
482
- return flag
476
+ if not get_executable_info ("tex" ):
477
+ warnings .warn ("Setting matplotlibrc text.usetex requires TeX." )
478
+ return False
479
+ if not get_executable_info ("dvipng" ):
480
+ warnings .warn ("Setting matplotlibrc text.usetex requires dvipng." )
481
+ return False
482
+ if not get_executable_info ("gs" ):
483
+ warnings .warn (
484
+ "Setting matplotlibrc text.usetex requires ghostscript." )
485
+ return False
486
+ return True
483
487
484
488
485
489
def _get_home ():
0 commit comments