diff --git a/doc/config.md b/doc/config.md index 947481e..b584ec1 100644 --- a/doc/config.md +++ b/doc/config.md @@ -203,6 +203,48 @@ This is a comma-separated list of extra libraries to install file tracking on. See the [file tracking](file_tracking.md) documentation for more details. +### Environment variables + +Environment variables to set can be specified using the multi-line option +``environment``. Each line represents one environment variable in the format +``name = value``: + +```INI +[pgfutils] +environment = + name1 = value1 + name2 = value2 +``` + +All names and values are processed and set as strings; any leading or trailing +spaces around the names or values will be stripped. Existing environment +variables of the same name will be overwritten. This means that if you specify +the same variable multiple times the last value will be used. For example, the +configuration + +```INI +[pgfutils] +environment = + name1 = value1 + name2 = value2 + name1 = value3 +``` + +will result in ``name1`` being set to ``value3``. The variables are set during +the call to ``setup_figure()``, so any libraries that read the environment +variables must be imported after this call. + +Note that the ``PGFUTILS_TRACK_FILES`` variable described in the [file tracking +documentation](file_tracking.md) can be configured through this option, for +example to output tracked files to stdout: + +```INI +[pgfutils] +environment = + PGFUTILS_TRACK_FILES = 1 +``` + + Path settings ------------- diff --git a/extras/pgfutils.cfg b/extras/pgfutils.cfg index e5f908d..f6b6ec7 100644 --- a/extras/pgfutils.cfg +++ b/extras/pgfutils.cfg @@ -114,6 +114,13 @@ preamble_substitute = false # Extra library-specific file trackers to install. extra_tracking = +# Environment variables to set. This should be a multi-line value; place one or +# more spaces between each subsequenct line (these spaces will be removed when +# parsing the configuration). Each line should be in the format name = value, +# with the value being read as a string, and leading and trailing spaces being +# removed from both the name and the value. +environment = + # Path setup. diff --git a/pgfutils.py b/pgfutils.py index e1f50a8..4c5958f 100644 --- a/pgfutils.py +++ b/pgfutils.py @@ -36,7 +36,7 @@ """ -__version__ = "1.4.0" +__version__ = "1.5.0" # We don't import Matplotlib here as this brings in NumPy. In turn, NumPy # caches a reference to the io.open() method as part of its data loading @@ -410,6 +410,7 @@ def _config_reset(): 'figure_background': '', 'axes_background': 'white', 'extra_tracking': '', + 'environment': '', }, 'paths': { @@ -705,24 +706,39 @@ def setup_figure(width=1.0, height=1.0, columns=None, margin=False, """ global _config, _interactive - # Need to install our file trackers (if desired) - # before we import Matplotlib. - if 'PGFUTILS_TRACK_FILES' in os.environ: - _install_standard_file_trackers() - # Reset the configuration. _config_reset() # Load configuration from a local file if one exists. if os.path.exists('pgfutils.cfg'): _config.read('pgfutils.cfg') + _file_tracker.filenames.add(("r", "pgfutils.cfg")) # And anything given in the function call. if kwargs: _config.read_kwargs(**kwargs) - # Now we can add any extra trackers specified in the config. + # Set environment variables specified in the configuration. + for line in _config['pgfutils']['environment'].splitlines(): + line = line.strip() + if not line: + continue + + # Check the variables are formatted correctly. + if '=' not in line: + raise ValueError( + "Environment variables should be in the form NAME=VALUE. " + "The line '{}' does not match this.".format(line) + ) + + # And set them. + key, value = line.split("=", 1) + os.environ[key.strip()] = value.strip() + + # Install file trackers if desired. This must be done before anything which + # imports Matplotlib. if 'PGFUTILS_TRACK_FILES' in os.environ: + _install_standard_file_trackers() extra = _config['pgfutils']['extra_tracking'].strip() if extra: _install_extra_file_trackers(extra.split(",")) diff --git a/tests/sources/environment/check_set/basic.py b/tests/sources/environment/check_set/basic.py new file mode 100644 index 0000000..3e4d035 --- /dev/null +++ b/tests/sources/environment/check_set/basic.py @@ -0,0 +1,14 @@ +from pgfutils import setup_figure, save +setup_figure() + +import os + +name1 = os.environ.get("name1", "not set") +name2 = os.environ.get("name2", "not set") + +if name1 != "value1": + raise ValueError("Incorrect value ({}) for environment variable name1".format(name1)) +if name2 != "value2": + raise ValueError("Incorrect value ({}) for environment variable name2".format(name2)) + +save() diff --git a/tests/sources/environment/check_set/override.py b/tests/sources/environment/check_set/override.py new file mode 100644 index 0000000..7932138 --- /dev/null +++ b/tests/sources/environment/check_set/override.py @@ -0,0 +1,17 @@ +import os +os.environ["name1"] = "original value" + +from pgfutils import setup_figure, save +setup_figure() + +import os + +name1 = os.environ.get("name1", "not set") +name2 = os.environ.get("name2", "not set") + +if name1 != "value1": + raise ValueError("Incorrect value ({}) for environment variable name1".format(name1)) +if name2 != "value2": + raise ValueError("Incorrect value ({}) for environment variable name2".format(name2)) + +save() diff --git a/tests/sources/environment/check_set/pgfutils.cfg b/tests/sources/environment/check_set/pgfutils.cfg new file mode 100644 index 0000000..bada1aa --- /dev/null +++ b/tests/sources/environment/check_set/pgfutils.cfg @@ -0,0 +1,4 @@ +[pgfutils] +environment= + name1=value1 + name2 = value2 diff --git a/tests/sources/environment/invalid/basic.py b/tests/sources/environment/invalid/basic.py new file mode 100644 index 0000000..84dd1f0 --- /dev/null +++ b/tests/sources/environment/invalid/basic.py @@ -0,0 +1,3 @@ +from pgfutils import setup_figure, save +setup_figure() +save() diff --git a/tests/sources/environment/invalid/pgfutils.cfg b/tests/sources/environment/invalid/pgfutils.cfg new file mode 100644 index 0000000..f6c38eb --- /dev/null +++ b/tests/sources/environment/invalid/pgfutils.cfg @@ -0,0 +1,4 @@ +[pgfutils] +environment= + line1=ok + line2 should fail diff --git a/tests/sources/environment/repeated/basic.py b/tests/sources/environment/repeated/basic.py new file mode 100644 index 0000000..4b02dfe --- /dev/null +++ b/tests/sources/environment/repeated/basic.py @@ -0,0 +1,14 @@ +from pgfutils import setup_figure, save +setup_figure() + +import os + +name1 = os.environ.get("name1", "not set") +name2 = os.environ.get("name2", "not set") + +if name1 != "value3": + raise ValueError("Incorrect value ({}) for environment variable name1".format(name1)) +if name2 != "value2": + raise ValueError("Incorrect value ({}) for environment variable name2".format(name2)) + +save() diff --git a/tests/sources/environment/repeated/pgfutils.cfg b/tests/sources/environment/repeated/pgfutils.cfg new file mode 100644 index 0000000..8d468f5 --- /dev/null +++ b/tests/sources/environment/repeated/pgfutils.cfg @@ -0,0 +1,5 @@ +[pgfutils] +environment= + name1=value1 + name2 = value2 + name1=value3 diff --git a/tests/sources/tracking/config_enabled/config_enabled.py b/tests/sources/tracking/config_enabled/config_enabled.py new file mode 100644 index 0000000..caa703c --- /dev/null +++ b/tests/sources/tracking/config_enabled/config_enabled.py @@ -0,0 +1,20 @@ +from pgfutils import setup_figure, add_dependencies, save +setup_figure(width=1, height=1) + +import numpy as np +from matplotlib import pyplot as plt +import os.path + +noise = np.load('noise.npy') +plt.imshow(noise) +plt.colorbar() + +data = np.loadtxt('scatter.csv', delimiter=',', dtype=np.int) + +x = data[:, :3] +y = data[:, 3:] + +plt.scatter(x, y) + +add_dependencies("extra.file") +save() diff --git a/tests/sources/tracking/config_enabled/noise.npy b/tests/sources/tracking/config_enabled/noise.npy new file mode 100644 index 0000000..455a082 Binary files /dev/null and b/tests/sources/tracking/config_enabled/noise.npy differ diff --git a/tests/sources/tracking/config_enabled/pgfutils.cfg b/tests/sources/tracking/config_enabled/pgfutils.cfg new file mode 100644 index 0000000..f4951c7 --- /dev/null +++ b/tests/sources/tracking/config_enabled/pgfutils.cfg @@ -0,0 +1,3 @@ +[pgfutils] +environment = + PGFUTILS_TRACK_FILES = 1 diff --git a/tests/sources/tracking/config_enabled/scatter.csv b/tests/sources/tracking/config_enabled/scatter.csv new file mode 100644 index 0000000..a7f37ef --- /dev/null +++ b/tests/sources/tracking/config_enabled/scatter.csv @@ -0,0 +1,30 @@ + -41, 8, 40, 63, 11, -20 + 198, -4, -173, -19, -107, -96 +-127, -137, -19, -49, -65, 79 + 302, 116, -23, -22, 23, -65 + -97, 138, -85, -13, 10, -75 +-167, 82, 34, 41, 31, -10 + 73, -71, 92, 59, 55, 21 + -18, 268, 11, 101, -57, -1 + 101, -41, -58, -34, -63, -19 + 2, -80, -146, 31, 18, 65 + 54, -3, 23, 45, -5, -36 + 63, 76, 199, -3, -21, 58 +-185, -7, -32, -58, -20, -30 + 100, 91, 53, 29, -105, 16 + -30, 37, 281, 15, 34, -41 +-146, -204, -37, 12, 9, -77 + 22, 78, -34, -2, 68, -100 + -97, 14, -9, -42, 90, -36 + -5, 213, -73, -14, -15, -11 + 51, 2, -102, 26, 23, -30 + -13, -30, -242, -30, -27, -45 + -5, -18, 19, -78, 129, 7 + -84, -76, -92, -34, 10, -7 + 72, 20, -35, -49, -8, -52 + 27, -5, 102, 0, 3, -66 + 128, 93, -121, -28, -15, 21 + -77, 11, -150, -15, -18, 72 + -53, -7, -137, 17, 41, 37 + 98, -46, 224, -20, -43, 0 +-158, -15, -98, -21, 12, 26 diff --git a/tests/test_environment.py b/tests/test_environment.py new file mode 100644 index 0000000..e9c0318 --- /dev/null +++ b/tests/test_environment.py @@ -0,0 +1,34 @@ +import os.path +from .utils import build_figure, clean_dir +import pytest + +srcdir = os.path.join(os.path.normpath(os.path.dirname(__file__)), "sources", "environment") + + +def test_environment_set(): + """Check environment variables can be set...""" + res = build_figure(os.path.join(srcdir, "check_set"), "basic.py") + assert res.returncode == 0, "Environment variables not set correctly." + clean_dir(srcdir) + + +def test_environment_override(): + """Check environment variables can override existing ones...""" + res = build_figure(os.path.join(srcdir, "check_set"), "override.py") + assert res.returncode == 0, "Environment variables not set correctly." + clean_dir(srcdir) + + +def test_environment_repeated(): + """Check the last value of repeated environment variables is used...""" + res = build_figure(os.path.join(srcdir, "repeated"), "basic.py") + assert res.returncode == 0, "Environment variables not set correctly." + clean_dir(srcdir) + + +def test_environment_invalid(): + """Check invalid environment variable in configuration is rejected...""" + res = build_figure(os.path.join(srcdir, "invalid"), "basic.py") + assert res.returncode != 0, "Configuration not rejected." + assert "Environment variables should be in the form" in res.stderr, "Incorrect message." + clean_dir(srcdir) diff --git a/tests/test_tracking.py b/tests/test_tracking.py index ffbc046..7e7b015 100644 --- a/tests/test_tracking.py +++ b/tests/test_tracking.py @@ -534,3 +534,23 @@ def test_import_tracking_extra_imports(self): } assert actual == expected self.cleanup() + + + def test_track_enabled_config(self): + """File tracking can be enabled through configured environment variables...""" + # Run the script. + res = build_figure(os.path.join(dirname, "config_enabled"), 'config_enabled.py') + assert res.returncode == 0 + + # Check the results are as expected. + expected = { + 'r:pgfutils.cfg', + 'r:noise.npy', + 'r:scatter.csv', + 'r:extra.file', + 'w:config_enabled-img0.png', + 'w:config_enabled-img1.png', + } + actual = set(res.stdout.strip().splitlines()) + assert actual == expected + self.cleanup()