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