diff --git a/ChangeLog b/ChangeLog
index 4ef00ca..55b40c1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,8 @@ ChangeLog
 *New:*
 
     * Add getfloat() method
+    * Allow globs in `config_files`
+    * <PROJECT>_CONFIG env var will now have the same behaviour than `config_files` items
 
 
 1.2.1 (2014-10-24)
diff --git a/README.rst b/README.rst
index 61d8638..5601b1f 100644
--- a/README.rst
+++ b/README.rst
@@ -94,7 +94,7 @@ Features
 --------
 
 **Env-based configuration files**
-    An extra configuration file can be provided through ``MYPROJ_CONFIG``;
+    An extra configuration file/directory/glob can be provided through ``MYPROJ_CONFIG``;
     it takes precedence over other files
 
 **Default options**
diff --git a/docs/reference.rst b/docs/reference.rst
index 6bf9e47..dd5d19b 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -16,7 +16,9 @@ The ``ConfigGetter`` class
                           will be loaded.
     :param list config_files: List of ini-style configuration files to use.
                               Each item may either be the path to a simple file, or to a directory
-                              (if the path ends with a '/'). Each directory path will be replaced by the list of
+                              (if the path ends with a '/') or a glob pattern (which will select all the files
+                              matching the pattern according to the rules used by the shell).
+                              Each directory path will be replaced by the list of
                               its directly contained files, in alphabetical order, excluding those whose name
                               starts with a '.'.
                               Provided configuration files are read in the order their name was provided,
diff --git a/getconf/base.py b/getconf/base.py
index 1d61b67..8c7908e 100644
--- a/getconf/base.py
+++ b/getconf/base.py
@@ -67,27 +67,31 @@ class ConfigGetter(object):
         self.namespace = namespace
         self.defaults = defaults or {}
 
-        final_config_files = []
-        for path in config_files:
+        self.search_files = []
+        extra_config_file = os.environ.get(self._env_key('config'), None)
+
+        for path in list(config_files) + [extra_config_file]:
+            if path is None:
+                continue
             # Handle '~/.foobar.conf'
             path = os.path.abspath(os.path.expanduser(path))
             if os.path.isdir(path):
-                directory_files = glob.glob(os.path.join(path, '*'))
+                path = os.path.join(path, '*')
+
+            self.search_files.append(path)
+
+        final_config_files = []
+        for path in self.search_files:
+            directory_files = glob.glob(path)
+            if directory_files:
                 # Reverse order: final_config_files is parsed from left to right,
                 # so 99_foo naturally takes precedence over 10_base
                 final_config_files.extend(sorted(directory_files))
-            else:
-                final_config_files.append(path)
-
-        extra_config_file = os.environ.get(self._env_key('config'), '')
-        if extra_config_file:
-            final_config_files.append(extra_config_file)
 
-        self.search_files = final_config_files
         # ConfigParser's precedence rules say "later files take precedence over previous ones".
         # Since our final_config_files are sorted from least important to most important,
         # that's exactly what we need.
-        self.found_files = self.parser.read(self.search_files)
+        self.found_files = self.parser.read(final_config_files)
 
         logger.info(
             "Successfully loaded configuration from files %r (searching in %r)",

Load particular files from a directory
When using ``ansible`` to deploy a new configuration, ``ansible`` will create backups of the files it plans to update.
Here, this would be ``/path/to/file.ini.20150218``, which *will* be loaded by ``getconf``.

I'd like ``getconf`` to only load ``*.ini`` files.

(original french message below)
> Lorsqu'ansible déploie une configuration il cré systématiquement un backup des fichiers à modifier. Dans le cas de la configuration, il cré un fichier `SINAME.ini.DATE` ce qui peut poser problème si on charge tous les fichiers d'un répertoire.
>
> Il ne faudrait donc charger que les fichiers `*.ini`
diff --git a/tests/test_base.py b/tests/test_base.py
index c1e9ba6..17673ce 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -106,7 +106,7 @@ class ConfigGetterTestCase(unittest.TestCase):
     def test_from_directory(self):
         """Test fetching from a directory."""
         getter = getconf.ConfigGetter('TESTNS', config_files=[self.example_directory])
-        self.assertEqual([self.example_path, self.example2_path], getter.search_files)
+        self.assertEqual([os.path.join(self.example_directory, '*')], getter.search_files)
         self.assertEqual([self.example_path, self.example2_path], getter.found_files)
         with Environ(TESTNS_FOO='blah'):
             # A non-file-defined value
@@ -123,7 +123,7 @@ class ConfigGetterTestCase(unittest.TestCase):
     def test_from_directory_and_files(self):
         """Test fetching from both directories and files"""
         getter = getconf.ConfigGetter('TESTNS', [self.example_directory, self.example_path])
-        self.assertEqual([self.example_path, self.example2_path, self.example_path], getter.search_files)
+        self.assertEqual([os.path.join(self.example_directory, '*'), self.example_path], getter.search_files)
         self.assertEqual([self.example_path, self.example2_path, self.example_path], getter.found_files)
         with Environ(TESTNS_FOO='blah'):
             # A non-file-defined value
@@ -137,6 +137,36 @@ class ConfigGetterTestCase(unittest.TestCase):
             # A section.key defined in the base file, not overridden
             self.assertEqual('21', getter.get('section1.otherfoo'))
 
+    def test_from_globs(self):
+        """Test fetching from globs"""
+        getter = getconf.ConfigGetter('TESTNS', [os.path.join(self.example_directory, '*2.ini')])
+        self.assertEqual([os.path.join(self.example_directory, '*2.ini')], getter.search_files)
+        self.assertEqual([self.example2_path], getter.found_files)
+        with Environ(TESTNS_FOO='blah'):
+            # A non-file-defined value
+            self.assertEqual('blah', getter.get('foo', 'foo'))
+            # A sectionless file-defined key
+            self.assertEqual('', getter.get('bar'))
+            # A section.key file-defined in all three => example wins
+            self.assertEqual('24', getter.get('section1.foo'))
+            # A section.key defined in the second file
+            self.assertEqual('13', getter.get('section2.bar'))
+            # A section.key defined in the base file, not overridden
+            self.assertEqual('', getter.get('section1.otherfoo'))
+
+    def test_environ_defined_globs(self):
+        """Test reading from an environment-defined config globs"""
+        with Environ(TESTNS_CONFIG=os.path.join(self.example_directory, '*2.ini'), TESTNS_FOO='blah'):
+            getter = getconf.ConfigGetter('TESTNS', [])
+            self.assertEqual([os.path.join(self.example_directory, '*2.ini')], getter.search_files)
+            self.assertEqual([self.example2_path], getter.found_files)
+            # A non-file-defined value
+            self.assertEqual('blah', getter.get('foo', 'foo'))
+            # A sectionless file-defined key
+            self.assertEqual('', getter.get('bar'))
+            # A section.key file-defined
+            self.assertEqual('24', getter.get('section1.foo'))
+
     def test_environ_defined_file(self):
         """Test reading from an environment-defined config file."""
         with Environ(TESTNS_CONFIG=self.example_path, TESTNS_FOO='blah'):

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y gcc
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
exceptiongroup==1.2.2
execnet==2.1.1
-e git+https://github.com/Polyconseil/getconf.git@28028bd9d85df0486e6c5c8f5bb25a9415c06816#egg=getconf
iniconfig==2.1.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
tomli==2.2.1
typing_extensions==4.13.0

name: getconf
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - tomli==2.2.1
      - typing-extensions==4.13.0
prefix: /opt/conda/envs/getconf

tests/test_base.py::ConfigGetterTestCase::test_environ_defined_globs tests/test_base.py::ConfigGetterTestCase::test_from_directory tests/test_base.py::ConfigGetterTestCase::test_from_directory_and_files tests/test_base.py::ConfigGetterTestCase::test_from_globs
tests/test_base.py::ConfigGetterTestCase::test_defaults tests/test_base.py::ConfigGetterTestCase::test_defaults_with_directory tests/test_base.py::ConfigGetterTestCase::test_defaults_with_file tests/test_base.py::ConfigGetterTestCase::test_environ_defined_file tests/test_base.py::ConfigGetterTestCase::test_environ_overrides_config tests/test_base.py::ConfigGetterTestCase::test_environ_overrides_default tests/test_base.py::ConfigGetterTestCase::test_environ_section tests/test_base.py::ConfigGetterTestCase::test_environ_settings tests/test_base.py::ConfigGetterTestCase::test_file_overide_default tests/test_base.py::ConfigGetterTestCase::test_get_section_env tests/test_base.py::ConfigGetterTestCase::test_get_section_file tests/test_base.py::ConfigGetterTestCase::test_getbool_badvalue tests/test_base.py::ConfigGetterTestCase::test_getbool_empty tests/test_base.py::ConfigGetterTestCase::test_getbool_false tests/test_base.py::ConfigGetterTestCase::test_getbool_nonempty_default tests/test_base.py::ConfigGetterTestCase::test_getbool_true tests/test_base.py::ConfigGetterTestCase::test_getfloat_bad_format tests/test_base.py::ConfigGetterTestCase::test_getfloat_bad_value tests/test_base.py::ConfigGetterTestCase::test_getfloat_empty_value tests/test_base.py::ConfigGetterTestCase::test_getfloat_value tests/test_base.py::ConfigGetterTestCase::test_getint_value tests/test_base.py::ConfigGetterTestCase::test_getlist_dirty tests/test_base.py::ConfigGetterTestCase::test_getlist_empty tests/test_base.py::ConfigGetterTestCase::test_getlist_multi tests/test_base.py::ConfigGetterTestCase::test_getlist_nonempty_default tests/test_base.py::ConfigGetterTestCase::test_getlist_single tests/test_base.py::ConfigGetterTestCase::test_named_arguments tests/test_base.py::ConfigGetterTestCase::test_no_settings tests/test_base.py::ConfigGetterTestCase::test_nonexistent_file tests/test_base.py::ConfigGetterTestCase::test_real_file tests/test_base.py::ConfigGetterTestCase::test_real_files tests/test_base.py::ConfigGetterTestCase::test_sub_section tests/test_base.py::ConfigGetterTestCase::test_unicode
README.rst getconf/base.py ChangeLog docs/reference.rst
README.rst getconf/base.py ChangeLog docs/reference.rst