Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

depr: deprecate utils.strings #1031

Merged
merged 8 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/pymovements/_utils/_strings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# 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 string specific funtions."""
from __future__ import annotations

import re

CURLY_TO_REGEX = re.compile(
r'(?:^|(?<=[^{])|(?<={{)){(?P<name>[^{][0-9a-zA-z_]*?)(?:\:(?P<quantity>\d*)(?P<type>[sd])?)?}',
)


def curly_to_regex(s: str) -> re.Pattern:
"""Return regex pattern converted from provided python formatting style pattern.

By default all parameters are strings, if you want to specify number you can do: {num:d}
If you want to specify parameter's length you can do: {two_symbols:2} or {four_digits:4d}
Characters { and } can be escaped the same way as in python: {{ and }}
For example:
r'{subject_id:d}_{session_name}.csv'
converts to r'(?P<subject_id>[0-9]+)_(?P<session_name>.+).csv'

Parameters
----------
s: str
Pattern in python formatting style.

Returns
-------
re.Pattern
Converted regex patterns.
"""

def replace_aux(match: re.Match) -> str: # Auxiliary replacement function
pattern = r'.'
if match.group('type') == 'd':
pattern = r'[0-9]'
elif match.group('type') == 's':
pattern = r'.'
quantity = r'+'
if match.group('quantity'):
quantity = f'{{{match.group("quantity")}}}'
return fr'(?P<{match.group("name")}>{pattern}{quantity})'

result = CURLY_TO_REGEX.sub(replace_aux, s)
result = result.replace('{{', '{').replace('}}', '}')
return re.compile(result)
2 changes: 1 addition & 1 deletion src/pymovements/dataset/dataset_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from tqdm.auto import tqdm

from pymovements._utils._paths import match_filepaths
from pymovements._utils._strings import curly_to_regex
from pymovements.dataset.dataset_definition import DatasetDefinition
from pymovements.dataset.dataset_paths import DatasetPaths
from pymovements.events import EventDataFrame
Expand All @@ -37,7 +38,6 @@
from pymovements.gaze.io import from_csv
from pymovements.gaze.io import from_ipc
from pymovements.reading_measures import ReadingMeasures
from pymovements.utils.strings import curly_to_regex


def scan_dataset(definition: DatasetDefinition, paths: DatasetPaths) -> pl.DataFrame:
Expand Down
2 changes: 1 addition & 1 deletion src/pymovements/stimulus/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
from pathlib import Path

from pymovements._utils._paths import get_filepaths
from pymovements._utils._strings import curly_to_regex
from pymovements.utils.plotting import draw_image_stimulus
from pymovements.utils.strings import curly_to_regex


class ImageStimulus:
Expand Down
35 changes: 16 additions & 19 deletions src/pymovements/utils/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,24 @@
# 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 string specific funtions."""
"""Provides string specific funtions.

.. deprecated:: v0.21.1
This module will be removed in v0.26.0.
"""
from __future__ import annotations

import re

CURLY_TO_REGEX = re.compile(
r'(?:^|(?<=[^{])|(?<={{)){(?P<name>[^{][0-9a-zA-z_]*?)(?:\:(?P<quantity>\d*)(?P<type>[sd])?)?}',
)
from deprecated.sphinx import deprecated

from pymovements._utils._strings import curly_to_regex as _curly_to_regex


@deprecated(
reason='This module will be removed in v0.26.0.',
version='v0.21.1',
)
def curly_to_regex(s: str) -> re.Pattern:
"""Return regex pattern converted from provided python formatting style pattern.

Expand All @@ -37,6 +45,9 @@ def curly_to_regex(s: str) -> re.Pattern:
r'{subject_id:d}_{session_name}.csv'
converts to r'(?P<subject_id>[0-9]+)_(?P<session_name>.+).csv'

.. deprecated:: v0.21.1
This module will be removed in v0.26.0.

Parameters
----------
s: str
Expand All @@ -47,18 +58,4 @@ def curly_to_regex(s: str) -> re.Pattern:
re.Pattern
Converted regex patterns.
"""

def replace_aux(match: re.Match) -> str: # Auxiliary replacement function
pattern = r'.'
if match.group('type') == 'd':
pattern = r'[0-9]'
elif match.group('type') == 's':
pattern = r'.'
quantity = r'+'
if match.group('quantity'):
quantity = f'{{{match.group("quantity")}}}'
return fr'(?P<{match.group("name")}>{pattern}{quantity})'

result = CURLY_TO_REGEX.sub(replace_aux, s)
result = result.replace('{{', '{').replace('}}', '}')
return re.compile(result)
return _curly_to_regex(s=s)
96 changes: 96 additions & 0 deletions tests/unit/_utils/_strings_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# 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 string utilities."""
from __future__ import annotations

import re

import pytest

from pymovements._utils._strings import curly_to_regex


@pytest.mark.parametrize(
('pattern', 'expected_regex'),
[
pytest.param(
r'{subject_id:d}_{session_name}.csv',
re.compile(r'(?P<subject_id>[0-9]+)_(?P<session_name>.+).csv'),
id='test_basic_pattern',
),
pytest.param(
r'',
re.compile(r''),
id='test_empty_string',
),
pytest.param(
r'test',
re.compile(r'test'),
id='test_no_curly_braces',
),
pytest.param(
r'{test}',
re.compile(r'(?P<test>.+)'),
id='test_one_curly_brace',
),
pytest.param(
r'{t3ST}',
re.compile(r'(?P<t3ST>.+)'),
id='test_various_characters_as_name',
),
pytest.param(
r'{test1}_{test2}',
re.compile(r'(?P<test1>.+)_(?P<test2>.+)'),
id='test_two_curly_braces',
),
pytest.param(
r'{{{test1}}}',
re.compile(r'{(?P<test1>.+)}'),
id='test_nested_curly_braces',
),
pytest.param(
r'{{test1}}',
re.compile(r'{test1}'),
id='test_escaped_curly_braces',
),
pytest.param(
r'{num:s}',
re.compile(r'(?P<num>.+)'),
id='test_explicit_string',
),
pytest.param(
r'{num:d}',
re.compile(r'(?P<num>[0-9]+)'),
id='test_numbers',
),
pytest.param(
r'{num:42}',
re.compile(r'(?P<num>.{42})'),
id='test_quantities',
),
pytest.param(
r'{num:5d}',
re.compile(r'(?P<num>[0-9]{5})'),
id='test_quantities_2',
),
],
)
def test_curly_to_regex(pattern, expected_regex):
assert curly_to_regex(pattern) == expected_regex
97 changes: 26 additions & 71 deletions tests/unit/utils/strings_test.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,80 +17,35 @@
# 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 string utilities."""
from __future__ import annotations

"""Tests deprecated utils.strings."""
import re

import pytest

from pymovements import __version__
from pymovements.utils.strings import curly_to_regex


@pytest.mark.parametrize(
('pattern', 'expected_regex'),
[
pytest.param(
r'{subject_id:d}_{session_name}.csv',
re.compile(r'(?P<subject_id>[0-9]+)_(?P<session_name>.+).csv'),
id='test_basic_pattern',
),
pytest.param(
r'',
re.compile(r''),
id='test_empty_string',
),
pytest.param(
r'test',
re.compile(r'test'),
id='test_no_curly_braces',
),
pytest.param(
r'{test}',
re.compile(r'(?P<test>.+)'),
id='test_one_curly_brace',
),
pytest.param(
r'{t3ST}',
re.compile(r'(?P<t3ST>.+)'),
id='test_various_characters_as_name',
),
pytest.param(
r'{test1}_{test2}',
re.compile(r'(?P<test1>.+)_(?P<test2>.+)'),
id='test_two_curly_braces',
),
pytest.param(
r'{{{test1}}}',
re.compile(r'{(?P<test1>.+)}'),
id='test_nested_curly_braces',
),
pytest.param(
r'{{test1}}',
re.compile(r'{test1}'),
id='test_escaped_curly_braces',
),
pytest.param(
r'{num:s}',
re.compile(r'(?P<num>.+)'),
id='test_explicit_string',
),
pytest.param(
r'{num:d}',
re.compile(r'(?P<num>[0-9]+)'),
id='test_numbers',
),
pytest.param(
r'{num:42}',
re.compile(r'(?P<num>.{42})'),
id='test_quantities',
),
pytest.param(
r'{num:5d}',
re.compile(r'(?P<num>[0-9]{5})'),
id='test_quantities_2',
),
],
)
def test_curly_to_regex(pattern, expected_regex):
assert curly_to_regex(pattern) == expected_regex
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
def test_curly_to_regex():
curly_to_regex('foo')


def test_curly_to_regex_deprecated():
with pytest.raises(DeprecationWarning):
curly_to_regex('foo')


def test_curly_to_regex_removed():
with pytest.raises(DeprecationWarning) as info:
curly_to_regex('foo')

regex = re.compile(r'.*will be removed in v(?P<version>[0-9]*[.][0-9]*[.][0-9]*)[.)].*')

msg = info.value.args[0]
remove_version = regex.match(msg).groupdict()['version']
current_version = __version__.split('+')[0]
assert current_version < remove_version, (
f'utils/strings.py was planned to be removed in v{remove_version}. '
f'Current version is v{current_version}.'
)
Loading