diff --git a/docs/configobj.rst b/docs/configobj.rst index 4456ab4..9fd6bde 100644 --- a/docs/configobj.rst +++ b/docs/configobj.rst @@ -281,9 +281,10 @@ ConfigObj takes the following arguments (with the default values shown) : * Nothing. In which case the ``filename`` attribute of your ConfigObj will be ``None``. You can set a filename at any time. - * A filename. What happens if the file doesn't already exist is determined by - the options ``file_error`` and ``create_empty``. The filename will be - preserved as the ``filename`` attribute. This can be changed at any time. + * A filename or pathlib.Path object. What happens if the file doesn't already + exist is determined by the options ``file_error`` and ``create_empty``. The + filename will be preserved as the ``filename`` attribute. This can be + changed at any time. * A list of lines. Any trailing newlines will be removed from the lines. The ``filename`` attribute of your ConfigObj will be ``None``. diff --git a/src/configobj/__init__.py b/src/configobj/__init__.py index 0d76ffc..0ae986a 100644 --- a/src/configobj/__init__.py +++ b/src/configobj/__init__.py @@ -18,6 +18,7 @@ import sys from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE +from pathlib import Path from ._version import __version__ @@ -1241,7 +1242,20 @@ def _load(self, infile, configspec): with open(infile, 'w') as h: h.write('') content = [] - + + elif isinstance(infile, Path): + self.filename = str(infile) + if infile.is_file(): + with infile.open('rb') as h: + content = h.readlines() or [] + elif self.file_error: + raise IOError('Config file not found: "%s".' % self.filename) + else: + if self.create_empty: + with infile.open('w') as h: + h.write('') + content = [] + elif isinstance(infile, (list, tuple)): content = list(infile) @@ -1275,7 +1289,7 @@ def set_section(in_section, this_section): # needs splitting into lines - but needs doing *after* decoding # in case it's not an 8 bit encoding else: - raise TypeError('infile must be a filename, file like object, or list of lines.') + raise TypeError('infile must be a filename, file like object, pathlib.Path or list of lines.') if content: # don't do it for the empty ConfigObj diff --git a/src/tests/test_configobj.py b/src/tests/test_configobj.py index eeb0a1c..37ddbf0 100644 --- a/src/tests/test_configobj.py +++ b/src/tests/test_configobj.py @@ -4,6 +4,7 @@ import re from codecs import BOM_UTF8 +from pathlib import Path from warnings import catch_warnings from tempfile import NamedTemporaryFile @@ -1104,6 +1105,24 @@ def test_creating_with_a_dictionary(): assert dictionary_cfg_content == cfg.dict() assert dictionary_cfg_content is not cfg.dict() +def test_reading_a_pathlib_path(cfg_contents): + cfg = cfg_contents(""" +[section] +foo = bar""") + c = ConfigObj(Path(cfg)) + assert 'foo' in c['section'] + +def test_creating_a_file_from_pathlib_path(tmp_path): + infile = tmp_path / 'config.ini' + assert not Path(tmp_path / 'config.ini').is_file() + c = ConfigObj(Path(infile), create_empty=True) + assert Path(tmp_path / 'config.ini').is_file() + +def test_creating_a_file_from_string(tmp_path): + infile = str(tmp_path / 'config.ini') + assert not Path(infile).is_file() + c = ConfigObj(infile, create_empty=True) + assert Path(infile).is_file() class TestComments(object): @pytest.fixture