Repository: aeye-lab/pymovements (python)

Issue:
deprecate utils modules that are not intended for end-users
## Description of the problem
Most of our utils modules are not really intended to be used by end-users , but should be used via high-level interfaces that call these methods.

This follows an anti-pattern where there is high coupling between the `utils` module and other modules and reduced cohesion inside modules like `dataset`.

Also this crowds our online documentation with irrelevant functions no end-user will need.

## Description of a solution

Instead, we should increase the cohesion inside each module and reduce coupling between modules.

Candidates are:
- [x] `utils.aois` #1080 
- [x] `utils.archives` -> `dataset._utils.archives` #1028 
- [x] `utils.checks` -> `_utils._checks` #1094 
- [x] remove `utils.decorators` #1008 
- [x] `utils.downloads` -> `dataset._utils.downloads` #1030 
- [x] `utils.filters` -> `events._utils._filters` #1032
- [x] `utils.parsing` -> `gaze._utils.parsing` #976 
- [x] `utils.paths` -> `_utils._paths` #1029 
- [ ] `utils.plotting` -> `plotting._utils.matlab` #1081 
- [x] `utils.strings` -> `_utils._strings` #1031 


Pull request:
depr: deprecate `utils.paths`
## :warning: Deprecation

`utils.paths` is deprecated and will be removed in pymovements v0.26.0

## Description

Deprecate `utils.paths` as none of the included are intended for the end-user.

The following warnings are printed:

```
DeprecationWarning: Call to deprecated function (or staticmethod) get_filepaths. (This function will be removed in v0.26.0.) -- Deprecated since version v0.21.0.
```

```
DeprecationWarning: Call to deprecated function (or staticmethod) match_filepaths. (This function will be removed in v0.26.0.) -- Deprecated since version v0.21.0.
```


partly resolves #999

## Implemented changes

- [x] moved `utils/paths.py` to `_utils/_paths.py` and adjusted to new location
- [x] use `_utils.paths` in remaining source
- [x] created deprecated function in `utils/paths.py`
- [x] moved `tests/unit/utils/paths_test.py` to `tests/unit/_utils/_paths_test.py`
- [x] add test that functions are correctly deprecated
- [x] add test that functions are removed in planned version


Patch:
diff --git a/src/pymovements/_utils/__init__.py b/src/pymovements/_utils/__init__.py
new file mode 100644
index 0000000..6786db2
--- /dev/null
+++ b/src/pymovements/_utils/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2025 The pymovements Project Authors
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
diff --git a/src/pymovements/_utils/_paths.py b/src/pymovements/_utils/_paths.py
new file mode 100644
index 0000000..2893439
--- /dev/null
+++ b/src/pymovements/_utils/_paths.py
@@ -0,0 +1,139 @@
+# Copyright (c) 2022-2025 The pymovements Project Authors
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+"""Provides path specific funtions."""
+from __future__ import annotations
+
+import re
+from pathlib import Path
+
+
+def get_filepaths(
+        path: str | Path,
+        extension: str | list[str] | None = None,
+        regex: re.Pattern | None = None,
+) -> list[Path]:
+    """Get filepaths from rootpath depending on extension or regular expression.
+
+    Passing extension and regex is mutually exclusive.
+
+    Parameters
+    ----------
+    path: str | Path
+        Root path to be traversed.
+    extension: str | list[str] | None
+        File extension to be filtered for. (default: None)
+    regex: re.Pattern | None
+        Regular expression filenames will be filtered for. (default: None)
+
+    Returns
+    -------
+    list[Path]
+
+    Raises
+    ------
+    ValueError
+        If both extension and regex is being passed.
+    """
+    if extension is not None and regex is not None:
+        raise ValueError('extension and regex are mutually exclusive')
+
+    if extension is not None and isinstance(extension, str):
+        extension = [extension]
+
+    path = Path(path)
+    if not path.is_dir():
+        return []
+
+    filepaths = []
+    for childpath in path.iterdir():
+        if childpath.is_dir():
+            filepaths.extend(get_filepaths(path=childpath, extension=extension, regex=regex))
+        else:
+            # if extension specified and not matching, continue to next
+            if extension and childpath.suffix not in extension:
+                continue
+            # if regex specified and not matching, continue to next
+            if regex and not regex.match(childpath.name):
+                continue
+            filepaths.append(childpath)
+    return filepaths
+
+
+def match_filepaths(
+        path: str | Path,
+        regex: re.Pattern,
+        relative: bool = True,
+        relative_anchor: Path | None = None,
+) -> list[dict[str, str]]:
+    """Traverse path and match regular expression.
+
+    Parameters
+    ----------
+    path: str | Path
+        Root path to be traversed.
+    regex: re.Pattern
+        Regular expression filenames will be matched against.
+    relative: bool
+        If True, specify filepath as relative to root path. (default: True)
+    relative_anchor: Path | None
+        Specifies root path in case of ``relative == True``. If None, ``path`` will be chosen
+        as `relative_anchor` for recursive calls. (default: None)
+
+    Returns
+    -------
+    list[dict[str, str]]
+        Each entry contains the match group dictionary of the regular expression.
+
+    Raises
+    ------
+    ValueError
+        If ``path`` does not point to a directory.
+    """
+    path = Path(path)
+
+    if not path.exists():
+        raise ValueError(f'path does not exist (path = {path})')
+
+    if not path.is_dir():
+        raise ValueError(f'path must point to a directory (path = {path})')
+
+    if relative and relative_anchor is None:
+        relative_anchor = path
+
+    match_dicts: list[dict[str, str]] = []
+    for childpath in path.iterdir():
+        if childpath.is_dir():
+            recursive_results = match_filepaths(
+                path=childpath, regex=regex,
+                relative=relative, relative_anchor=relative_anchor,
+            )
+            match_dicts.extend(recursive_results)
+        elif match := regex.match(childpath.name):
+            match_dict = match.groupdict()
+
+            filepath = childpath
+            if relative:
+                # mypy is unaware that 'relative_anchor' can never be None (l.116)
+                assert relative_anchor is not None
+                filepath = filepath.relative_to(relative_anchor)
+
+            match_dict['filepath'] = str(filepath)
+            match_dicts.append(match_dict)
+    return match_dicts
diff --git a/src/pymovements/dataset/dataset_files.py b/src/pymovements/dataset/dataset_files.py
index 7beac9b..05c1d31 100644
--- a/src/pymovements/dataset/dataset_files.py
+++ b/src/pymovements/dataset/dataset_files.py
@@ -27,6 +27,7 @@ from typing import Any
 import polars as pl
 from tqdm.auto import tqdm
 
+from pymovements._utils._paths import match_filepaths
 from pymovements.dataset.dataset_definition import DatasetDefinition
 from pymovements.dataset.dataset_paths import DatasetPaths
 from pymovements.events import EventDataFrame
@@ -36,7 +37,6 @@ from pymovements.gaze.io import from_asc
 from pymovements.gaze.io import from_csv
 from pymovements.gaze.io import from_ipc
 from pymovements.reading_measures import ReadingMeasures
-from pymovements.utils.paths import match_filepaths
 from pymovements.utils.strings import curly_to_regex
 
 
diff --git a/src/pymovements/stimulus/image.py b/src/pymovements/stimulus/image.py
index 6e2b15d..b6bd721 100644
--- a/src/pymovements/stimulus/image.py
+++ b/src/pymovements/stimulus/image.py
@@ -22,7 +22,7 @@ from __future__ import annotations
 
 from pathlib import Path
 
-from pymovements.utils.paths import get_filepaths
+from pymovements._utils._paths import get_filepaths
 from pymovements.utils.plotting import draw_image_stimulus
 from pymovements.utils.strings import curly_to_regex
 
diff --git a/src/pymovements/utils/archives.py b/src/pymovements/utils/archives.py
index 2c6d2c5..bbc3745 100644
--- a/src/pymovements/utils/archives.py
+++ b/src/pymovements/utils/archives.py
@@ -34,7 +34,7 @@ from typing import IO
 
 from tqdm import tqdm
 
-from pymovements.utils.paths import get_filepaths
+from pymovements._utils._paths import get_filepaths
 
 
 def extract_archive(
diff --git a/src/pymovements/utils/parsing.py b/src/pymovements/utils/parsing.py
index adb7114..3ec0298 100644
--- a/src/pymovements/utils/parsing.py
+++ b/src/pymovements/utils/parsing.py
@@ -19,7 +19,7 @@
 # SOFTWARE.
 """Module for parsing input data.
 
-.. deprecated:: v0.21.0
+.. deprecated:: v0.21.1
    Please use gaze.from_asc() instead of utils.parsing.
    This module will be removed in v0.26.0.
 """
@@ -37,7 +37,7 @@ from pymovements.gaze._utils.parsing import parse_eyelink as _parse_eyelink
 @deprecated(
     reason='Please use gaze.from_asc() instead of utils.parsing. '
            'This module will be removed in v0.26.0.',
-    version='v0.21.0',
+    version='v0.21.1',
 )
 def parse_eyelink(
         filepath: Path | str,
diff --git a/src/pymovements/utils/paths.py b/src/pymovements/utils/paths.py
index 2893439..06517b4 100644
--- a/src/pymovements/utils/paths.py
+++ b/src/pymovements/utils/paths.py
@@ -17,13 +17,26 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
-"""Provides path specific funtions."""
+"""Provides path specific funtions.
+
+.. deprecated:: v0.21.1
+   This module will be removed in v0.26.0.
+"""
 from __future__ import annotations
 
 import re
 from pathlib import Path
 
+from deprecated.sphinx import deprecated
+
+from pymovements._utils._paths import get_filepaths as _get_filepaths
+from pymovements._utils._paths import match_filepaths as _match_filepaths
+
 
+@deprecated(
+    reason='This function will be removed in v0.26.0.',
+    version='v0.21.1',
+)
 def get_filepaths(
         path: str | Path,
         extension: str | list[str] | None = None,
@@ -33,6 +46,9 @@ def get_filepaths(
 
     Passing extension and regex is mutually exclusive.
 
+    .. deprecated:: v0.21.1
+       This module will be removed in v0.26.0.
+
     Parameters
     ----------
     path: str | Path
@@ -51,31 +67,17 @@ def get_filepaths(
     ValueError
         If both extension and regex is being passed.
     """
-    if extension is not None and regex is not None:
-        raise ValueError('extension and regex are mutually exclusive')
-
-    if extension is not None and isinstance(extension, str):
-        extension = [extension]
-
-    path = Path(path)
-    if not path.is_dir():
-        return []
-
-    filepaths = []
-    for childpath in path.iterdir():
-        if childpath.is_dir():
-            filepaths.extend(get_filepaths(path=childpath, extension=extension, regex=regex))
-        else:
-            # if extension specified and not matching, continue to next
-            if extension and childpath.suffix not in extension:
-                continue
-            # if regex specified and not matching, continue to next
-            if regex and not regex.match(childpath.name):
-                continue
-            filepaths.append(childpath)
-    return filepaths
+    return _get_filepaths(
+        path=path,
+        extension=extension,
+        regex=regex,
+    )
 
 
+@deprecated(
+    reason='This function will be removed in v0.26.0.',
+    version='v0.21.1',
+)
 def match_filepaths(
         path: str | Path,
         regex: re.Pattern,
@@ -84,6 +86,9 @@ def match_filepaths(
 ) -> list[dict[str, str]]:
     """Traverse path and match regular expression.
 
+    .. deprecated:: v0.21.1
+       This module will be removed in v0.26.0.
+
     Parameters
     ----------
     path: str | Path
@@ -106,34 +111,9 @@ def match_filepaths(
     ValueError
         If ``path`` does not point to a directory.
     """
-    path = Path(path)
-
-    if not path.exists():
-        raise ValueError(f'path does not exist (path = {path})')
-
-    if not path.is_dir():
-        raise ValueError(f'path must point to a directory (path = {path})')
-
-    if relative and relative_anchor is None:
-        relative_anchor = path
-
-    match_dicts: list[dict[str, str]] = []
-    for childpath in path.iterdir():
-        if childpath.is_dir():
-            recursive_results = match_filepaths(
-                path=childpath, regex=regex,
-                relative=relative, relative_anchor=relative_anchor,
-            )
-            match_dicts.extend(recursive_results)
-        elif match := regex.match(childpath.name):
-            match_dict = match.groupdict()
-
-            filepath = childpath
-            if relative:
-                # mypy is unaware that 'relative_anchor' can never be None (l.116)
-                assert relative_anchor is not None
-                filepath = filepath.relative_to(relative_anchor)
-
-            match_dict['filepath'] = str(filepath)
-            match_dicts.append(match_dict)
-    return match_dicts
+    return _match_filepaths(
+        path=path,
+        regex=regex,
+        relative=relative,
+        relative_anchor=relative_anchor,
+    )


Test patch:
diff --git a/tests/unit/_utils/_paths_test.py b/tests/unit/_utils/_paths_test.py
new file mode 100644
index 0000000..7e7fcf8
--- /dev/null
+++ b/tests/unit/_utils/_paths_test.py
@@ -0,0 +1,195 @@
+# Copyright (c) 2023-2025 The pymovements Project Authors
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+"""Test pymovements paths."""
+import pathlib
+import re
+import unittest
+from pathlib import Path
+
+import pytest
+
+from pymovements._utils._paths import get_filepaths
+from pymovements._utils._paths import match_filepaths
+
+
+def test_get_filepaths_mut_excl_extension_and_regex_error():
+    """Test mutually exclusive extension and regex in get_filepaths."""
+    with pytest.raises(ValueError) as excinfo:
+        get_filepaths(path='tmp', extension='extension', regex=re.compile('regex'))
+    msg, = excinfo.value.args
+    assert msg == 'extension and regex are mutually exclusive'
+
+
+@pytest.mark.parametrize(
+    ('extension', 'regex', 'expected_paths'),
+    [
+        pytest.param(
+            None,
+            re.compile('regex'),
+            [],
+            id='regex_empty_dir',
+        ),
+        pytest.param(
+            'extension',
+            None,
+            [],
+            id='extension_empty_dir',
+        ),
+    ],
+)
+def test_get_filepaths_empty_directory(
+        extension,
+        regex,
+        expected_paths,
+):
+    assert get_filepaths(path='tmp', extension=extension, regex=regex) == expected_paths
+
+
+def create_directory(tmp_path, sub_dirs, files):
+    for sub_dir in sub_dirs:
+        dir_path = tmp_path / sub_dir
+        dir_path.mkdir()
+        for file_name in files:
+            file_path = dir_path / file_name
+            file_path.write_text('test')
+
+
+@pytest.mark.parametrize(
+    ('extension', 'regex', 'sub_dirs', 'files', 'expected_paths'),
+    [
+        pytest.param(
+            '.txt',
+            None,
+            ['tmp_dir'],
+            ['foo.txt', 'bar.py'],
+            ['tmp_dir/foo.txt'],
+            id='extension_regex_get_filepaths_list',
+        ),
+        pytest.param(
+            None,
+            re.compile('foo'),
+            ['tmp_dir'],
+            ['foo.txt', 'foo.py', 'bar.py'],
+            ['tmp_dir/foo.txt', 'tmp_dir/foo.py'],
+            id='regex_get_filepaths_list',
+        ),
+    ],
+)
+def test_get_filepaths_expected_output(
+        extension,
+        regex,
+        tmp_path,
+        sub_dirs,
+        files,
+        expected_paths,
+):
+    create_directory(tmp_path, sub_dirs, files)
+    ret = get_filepaths(path=tmp_path, extension=extension, regex=regex)
+    expected_list = [tmp_path / pathlib.Path(expected_path) for expected_path in expected_paths]
+    assert sorted(ret) == sorted(expected_list)
+
+
+@pytest.mark.parametrize(
+    ('regex', 'sub_dirs', 'files', 'expected_dicts'),
+    [
+        pytest.param(
+            re.compile('.*'),
+            ['tmp_dir'],
+            [],
+            [],
+            id='empty_directory_empty_list',
+        ),
+        pytest.param(
+            re.compile('.*'),
+            ['tmp_dir'],
+            ['foo.txt'],
+            [{'filepath': str(Path('tmp_dir/foo.txt'))}],
+            id='match_all_no_groups_single_file',
+        ),
+        pytest.param(
+            re.compile('.*'),
+            ['tmp_dir'],
+            ['foo.txt', 'bar.py'],
+            [{'filepath': str(Path('tmp_dir/foo.txt'))}, {'filepath': str(Path('tmp_dir/bar.py'))}],
+            id='match_all_no_groups_two_files',
+        ),
+        pytest.param(
+            re.compile('foo'),
+            ['tmp_dir'],
+            ['foo.txt', 'bar.py'],
+            [{'filepath': str(Path('tmp_dir/foo.txt'))}],
+            id='match_substring_single_file_out_of_two_no_groups',
+        ),
+        pytest.param(
+            re.compile(r'(?P<foo>\d+)_(?P<bar>\d+).ext'),
+            ['a'],
+            ['123_456.ext', '456_789.ext'],
+            [
+                {'foo': '123', 'bar': '456', 'filepath': str(Path('a/123_456.ext'))},
+                {'foo': '456', 'bar': '789', 'filepath': str(Path('a/456_789.ext'))},
+            ],
+            id='match_groups_two_files',
+        ),
+    ],
+)
+def test_match_filepaths(
+        regex,
+        sub_dirs,
+        files,
+        expected_dicts,
+        tmp_path,
+):
+    create_directory(tmp_path, sub_dirs, files)
+    result_dicts = match_filepaths(path=tmp_path, regex=regex)
+
+    case = unittest.TestCase()
+    case.assertCountEqual(result_dicts, expected_dicts)
+
+
+def test_match_filepaths_not_relative(tmp_path):
+    create_directory(tmp_path, ['tmp_dir'], ['foo.txt'])
+    result_dicts = match_filepaths(path=tmp_path, regex=re.compile('.*'), relative=False)
+
+    expected_dicts = [{'filepath': str(tmp_path / 'tmp_dir/foo.txt')}]
+    case = unittest.TestCase()
+    case.assertCountEqual(result_dicts, expected_dicts)
+
+
+def test_match_filepaths_not_exists_raises_value_error(tmp_path):
+    filepath = tmp_path / 'not_existing'
+
+    with pytest.raises(ValueError) as excinfo:
+        match_filepaths(path=filepath, regex=re.compile('.*'))
+    msg, = excinfo.value.args
+
+    assert 'not exist' in msg
+    assert str(filepath) in msg
+
+
+def test_match_filepaths_no_directory_raises_value_error(tmp_path):
+    create_directory(tmp_path, ['tmp_dir'], ['foo.txt'])
+    filepath = tmp_path / 'tmp_dir' / 'foo.txt'
+
+    with pytest.raises(ValueError) as excinfo:
+        match_filepaths(path=filepath, regex=re.compile('.*'))
+    msg, = excinfo.value.args
+
+    assert 'must point to a directory' in msg
+    assert str(filepath) in msg
diff --git a/tests/unit/utils/paths_test.py b/tests/unit/utils/paths_test.py
index c1fb541..555791c 100644
--- a/tests/unit/utils/paths_test.py
+++ b/tests/unit/utils/paths_test.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023-2025 The pymovements Project Authors
+# Copyright (c) 2025 The pymovements Project Authors
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
@@ -17,179 +17,39 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
-"""Test pymovements paths."""
-import pathlib
+"""Tests deprecated utils.paths."""
 import re
-import unittest
-from pathlib import Path
 
 import pytest
 
+from pymovements import __version__
 from pymovements.utils.paths import get_filepaths
 from pymovements.utils.paths import match_filepaths
 
 
-def test_get_filepaths_mut_excl_extension_and_regex_error():
-    """Test mutually exclusive extension and regex in get_filepaths."""
-    with pytest.raises(ValueError) as excinfo:
-        get_filepaths(path='tmp', extension='extension', regex=re.compile('regex'))
-    msg, = excinfo.value.args
-    assert msg == 'extension and regex are mutually exclusive'
+@pytest.mark.filterwarnings('ignore::DeprecationWarning')
+@pytest.mark.parametrize('path_function', [get_filepaths, match_filepaths])
+def test_downloads_function(path_function, tmp_path):
+    path_function(path=tmp_path, regex=re.compile('foo'))
 
 
-@pytest.mark.parametrize(
-    ('extension', 'regex', 'expected_paths'),
-    [
-        pytest.param(
-            None,
-            re.compile('regex'),
-            [],
-            id='regex_empty_dir',
-        ),
-        pytest.param(
-            'extension',
-            None,
-            [],
-            id='extension_empty_dir',
-        ),
-    ],
-)
-def test_get_filepaths_empty_directory(
-        extension,
-        regex,
-        expected_paths,
-):
-    assert get_filepaths(path='tmp', extension=extension, regex=regex) == expected_paths
+@pytest.mark.parametrize('path_function', [get_filepaths, match_filepaths])
+def test_parse_eyelink_deprecated(path_function, tmp_path):
+    with pytest.raises(DeprecationWarning):
+        path_function(path=tmp_path, regex=re.compile('foo'))
 
 
-def create_directory(tmp_path, sub_dirs, files):
-    for sub_dir in sub_dirs:
-        dir_path = tmp_path / sub_dir
-        dir_path.mkdir()
-        for file_name in files:
-            file_path = dir_path / file_name
-            file_path.write_text('test')
+@pytest.mark.parametrize('path_function', [get_filepaths, match_filepaths])
+def test_parse_eyelink_removed(path_function, tmp_path):
+    with pytest.raises(DeprecationWarning) as info:
+        path_function(path=tmp_path, regex=re.compile('foo'))
 
+    regex = re.compile(r'.*will be removed in v(?P<version>[0-9]*[.][0-9]*[.][0-9]*)[.)].*')
 
-@pytest.mark.parametrize(
-    ('extension', 'regex', 'sub_dirs', 'files', 'expected_paths'),
-    [
-        pytest.param(
-            '.txt',
-            None,
-            ['tmp_dir'],
-            ['foo.txt', 'bar.py'],
-            ['tmp_dir/foo.txt'],
-            id='extension_regex_get_filepaths_list',
-        ),
-        pytest.param(
-            None,
-            re.compile('foo'),
-            ['tmp_dir'],
-            ['foo.txt', 'foo.py', 'bar.py'],
-            ['tmp_dir/foo.txt', 'tmp_dir/foo.py'],
-            id='regex_get_filepaths_list',
-        ),
-    ],
-)
-def test_get_filepaths_expected_output(
-        extension,
-        regex,
-        tmp_path,
-        sub_dirs,
-        files,
-        expected_paths,
-):
-    create_directory(tmp_path, sub_dirs, files)
-    ret = get_filepaths(path=tmp_path, extension=extension, regex=regex)
-    expected_list = [tmp_path / pathlib.Path(expected_path) for expected_path in expected_paths]
-    assert sorted(ret) == sorted(expected_list)
-
-
-@pytest.mark.parametrize(
-    ('regex', 'sub_dirs', 'files', 'expected_dicts'),
-    [
-        pytest.param(
-            re.compile('.*'),
-            ['tmp_dir'],
-            [],
-            [],
-            id='empty_directory_empty_list',
-        ),
-        pytest.param(
-            re.compile('.*'),
-            ['tmp_dir'],
-            ['foo.txt'],
-            [{'filepath': str(Path('tmp_dir/foo.txt'))}],
-            id='match_all_no_groups_single_file',
-        ),
-        pytest.param(
-            re.compile('.*'),
-            ['tmp_dir'],
-            ['foo.txt', 'bar.py'],
-            [{'filepath': str(Path('tmp_dir/foo.txt'))}, {'filepath': str(Path('tmp_dir/bar.py'))}],
-            id='match_all_no_groups_two_files',
-        ),
-        pytest.param(
-            re.compile('foo'),
-            ['tmp_dir'],
-            ['foo.txt', 'bar.py'],
-            [{'filepath': str(Path('tmp_dir/foo.txt'))}],
-            id='match_substring_single_file_out_of_two_no_groups',
-        ),
-        pytest.param(
-            re.compile(r'(?P<foo>\d+)_(?P<bar>\d+).ext'),
-            ['a'],
-            ['123_456.ext', '456_789.ext'],
-            [
-                {'foo': '123', 'bar': '456', 'filepath': str(Path('a/123_456.ext'))},
-                {'foo': '456', 'bar': '789', 'filepath': str(Path('a/456_789.ext'))},
-            ],
-            id='match_groups_two_files',
-        ),
-    ],
-)
-def test_match_filepaths(
-        regex,
-        sub_dirs,
-        files,
-        expected_dicts,
-        tmp_path,
-):
-    create_directory(tmp_path, sub_dirs, files)
-    result_dicts = match_filepaths(path=tmp_path, regex=regex)
-
-    case = unittest.TestCase()
-    case.assertCountEqual(result_dicts, expected_dicts)
-
-
-def test_match_filepaths_not_relative(tmp_path):
-    create_directory(tmp_path, ['tmp_dir'], ['foo.txt'])
-    result_dicts = match_filepaths(path=tmp_path, regex=re.compile('.*'), relative=False)
-
-    expected_dicts = [{'filepath': str(tmp_path / 'tmp_dir/foo.txt')}]
-    case = unittest.TestCase()
-    case.assertCountEqual(result_dicts, expected_dicts)
-
-
-def test_match_filepaths_not_exists_raises_value_error(tmp_path):
-    filepath = tmp_path / 'not_existing'
-
-    with pytest.raises(ValueError) as excinfo:
-        match_filepaths(path=filepath, regex=re.compile('.*'))
-    msg, = excinfo.value.args
-
-    assert 'not exist' in msg
-    assert str(filepath) in msg
-
-
-def test_match_filepaths_no_directory_raises_value_error(tmp_path):
-    create_directory(tmp_path, ['tmp_dir'], ['foo.txt'])
-    filepath = tmp_path / 'tmp_dir' / 'foo.txt'
-
-    with pytest.raises(ValueError) as excinfo:
-        match_filepaths(path=filepath, regex=re.compile('.*'))
-    msg, = excinfo.value.args
-
-    assert 'must point to a directory' in msg
-    assert str(filepath) in msg
+    msg = info.value.args[0]
+    remove_version = regex.match(msg).groupdict()['version']
+    current_version = __version__.split('+')[0]
+    assert current_version < remove_version, (
+        f'utils/paths.py was planned to be removed in v{remove_version}. '
+        f'Current version is v{current_version}.'
+    )