Skip to content

Commit

Permalink
Fix re replace issue (#468)
Browse files Browse the repository at this point in the history
* Fix re replace issue

Fixes #467

* Re-generate docs

* Bump version
  • Loading branch information
facelessuser authored Sep 30, 2024
1 parent 44c1c69 commit 657ae18
Show file tree
Hide file tree
Showing 15 changed files with 154 additions and 23 deletions.
4 changes: 4 additions & 0 deletions docs/src/markdown/about/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 4.21.1

- **FIX**: Fix issue with `re` replace in Python 3.12 and Python 3.13.

## 4.21

- **NEW**: Officially support Python 3.12 and Python 3.13, requires latest wxPython (4.2.2).
Expand Down
4 changes: 2 additions & 2 deletions docs/src/markdown/preferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ The **General** tab contains a couple of useful settings.
Single Instance

- By default, Rummage will allow for multiple windows to be open. If this option is enabled, the first window will be
be the only window to open. All subsequent instances will pass their arguments to the first and close without
showing a window.
the only window to open. All subsequent instances will pass their arguments to the first and close without showing
a window.

Language

Expand Down
2 changes: 1 addition & 1 deletion docs/src/markdown/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ options.

/// tip | Column Options
You can hide/show columns by right clicking the list header to get a special context menu. You can then deselect or
select the the column(s) you wish to hide/show respectively. You can also reorder the columns if desired.
select the column(s) you wish to hide/show respectively. You can also reorder the columns if desired.
///

## Regular Expression Tester
Expand Down
2 changes: 1 addition & 1 deletion rummage/lib/__meta__.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def parse_version(ver):


# (major, minor, micro, release type, pre-release build, post-release build, development-release)
__version_info__ = Version(4, 21, 0, 'final')
__version_info__ = Version(4, 21, 1, 'final')
__version__ = __version_info__._get_canonical()
__app__ = "Rummage"
__status__ = __version_info__[3]
Expand Down
2 changes: 1 addition & 1 deletion rummage/lib/gui/controls/custom_statusbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ContextMenu(wx.Menu):
"""Context Menu."""

def __init__(self, menu):
"""Attach the context menu to to the parent with the defined items."""
"""Attach the context menu to the parent with the defined items."""

wx.Menu.__init__(self)
self._callbacks = {}
Expand Down
2 changes: 1 addition & 1 deletion rummage/lib/gui/controls/dynamic_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def set_item_map(self, idx, *args):
self.size_sample -= 1

def get_map_item(self, idx, col=0, absolute=False):
"""Get attribute in in item map entry and the given index."""
"""Get attribute in item map entry and the given index."""

return self.itemDataMap[self.itemIndexMap[idx] if not absolute else idx][self.get_real_col(col)]

Expand Down
2 changes: 1 addition & 1 deletion rummage/lib/gui/controls/result_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class ContextMenu(wx.Menu):
"""Context Menu."""

def __init__(self, menu):
"""Attach the context menu to to the parent with the defined items."""
"""Attach the context menu to the parent with the defined items."""

wx.Menu.__init__(self)
self.create_menu(self, menu)
Expand Down
2 changes: 1 addition & 1 deletion rummage/lib/gui/data/docs/.dochash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0cd58713b67eafdcd920f80223baa5dc
ff7edfad8ee90d27d9cbc711c759338c
4 changes: 4 additions & 0 deletions rummage/lib/gui/data/docs/about/changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

<!-- Table of Content (Don't show for static sites) -->
<h1 id="changelog">Changelog</h1>
<h2 id="4211">4.21.1</h2>
<ul>
<li><strong>FIX</strong>: Fix issue with <code>re</code> replace in Python 3.12 and Python 3.13.</li>
</ul>
<h2 id="421">4.21</h2>
<ul>
<li><strong>NEW</strong>: Officially support Python 3.12 and Python 3.13, requires latest wxPython (4.2.2).</li>
Expand Down
4 changes: 2 additions & 2 deletions rummage/lib/gui/data/docs/preferences.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ <h2 id="general">General</h2>
<dl>
<dt>Single Instance</dt>
<dd>By default, Rummage will allow for multiple windows to be open. If this option is enabled, the first window will be
be the only window to open. All subsequent instances will pass their arguments to the first and close without
showing a window.</dd>
the only window to open. All subsequent instances will pass their arguments to the first and close without showing
a window.</dd>
<dt>Language</dt>
<dd>Rummage has internal support to display dialog labels in different languages. Currently Rummage has English. Russian
is outdated but includes a fair bit of the needed translations. See <a href="extras.html#localization">Localization</a> to
Expand Down
2 changes: 1 addition & 1 deletion rummage/lib/gui/data/docs/usage.html
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ <h3 id="results">Results</h3>
<div class="admonition tip">
<p class="admonition-title">Column Options</p>
<p>You can hide/show columns by right clicking the list header to get a special context menu. You can then deselect or
select the the column(s) you wish to hide/show respectively. You can also reorder the columns if desired.</p>
select the column(s) you wish to hide/show respectively. You can also reorder the columns if desired.</p>
</div>
<h2 id="regular-expression-tester">Regular Expression Tester</h2>
<p><img alt="Regex Tester" src="images/regex_tester.png" /></p>
Expand Down
2 changes: 1 addition & 1 deletion rummage/lib/gui/dialogs/rummage_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ def localize(self):
self.ERR_UPDATE = _("There was an error checking for updates!")
self.ERR_INVALID_SEARCH_PTH = _("Please enter a valid search path!")
self.ERR_INVALID_SEARCH = _("Please enter a valid search!")
self.ERR_EMPTY_CHAIN = _("There are no searches in this this chain!")
self.ERR_EMPTY_CHAIN = _("There are no searches in this chain!")
self.ERR_MISSING_SEARCH = _("'%s' is not found in saved searches!")
self.ERR_INVALID_CHAIN = _("Please enter a valid chain!")
self.ERR_INVALID_CHAIN_SEARCH = _("Saved search '%s' does not contain a valid search pattern!")
Expand Down
2 changes: 1 addition & 1 deletion rummage/lib/gui/util/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Color(Base):
"""Color object."""

def to_wxbgr(self, alpha=True):
"""Get the the wxPython RGB value."""
"""Get the wxPython RGB value."""

r, g, b = [alg.clamp(int(alg.round_half_up(c * 255)), 0, 255) for c in self.convert('srgb').coords(nans=False)]
a = alg.clamp(int(alg.round_half_up(self.alpha(nans=False)))) if alpha else 0xFF
Expand Down
12 changes: 2 additions & 10 deletions rummage/lib/rumcore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@
except ImportError: # pragma: no cover
REGEX_SUPPORT = False

if sys.version_info >= (3, 11):
import re._parser as _parser
else:
import sre_parse as _parser

# Common regex flags (re|regex)
IGNORECASE = 0x1 # (?i)
DOTALL = 0x2 # (?s)
Expand Down Expand Up @@ -309,7 +304,7 @@ class Search:
"""Search setup object."""

def __init__(self, replace=False):
"""Setup search object as as a search only or search and replace object."""
"""Setup search object as a search only or search and replace object."""

self._entry = []
self._is_replace = replace
Expand Down Expand Up @@ -747,9 +742,6 @@ def _findall(self, file_content, search_pattern, replace_pattern, flags, file_in
self.expand = pattern.compile(replace, (bre.FORMAT if bool(flags & FORMATREPLACE) else 0))
else:
pattern = _re_pattern(pattern, flags, self.is_binary)
if replace is not None and not self.is_plugin_replace:
template = _parser.parse_template(replace, pattern)
self.expand = lambda m, t=template: _parser.expand_template(t, m)

if REGEX_SUPPORT and isinstance(pattern, (bregex._REGEX_TYPE, bregex.Bregex)):
self.reverse = bool(pattern.flags & regex.REVERSE)
Expand Down Expand Up @@ -981,7 +973,7 @@ def search_and_replace(self):

if not self.abort and text:
# Update the file or buffer depending on what is being used.
# For a buffer, we will actually return the the content via a `BufferRecord`.
# For a buffer, we will actually return the content via a `BufferRecord`.
if is_buffer:
yield self._update_buffer(text)
file_record_sent = True
Expand Down
131 changes: 131 additions & 0 deletions tests/test_rumcore.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,137 @@ def test_literal_chain_search(self):
print(results)
self.assertEqual(len(results), 4)

def test_re_chain_replace(self):
"""Test for `re` search and replace."""

self.mktemp(
'searches.txt',
content=self.dedent(
'''
search1
search1
search2
search2
search3
search3
search1, search2, search3
'''
).encode('utf-8')
)

after = self.dedent(
'''
replace1
replace1
replace2
replace2
search3
search3
replace1, replace2, search3
'''
)

search_params = rc.Search(True)
search_params.add('search1', 'replace1', rc.IGNORECASE)
search_params.add('search2', 'replace2', rc.IGNORECASE)

file_id = 0
encoding = None
context = (0, 0)
flags = 0
backup_ext = 'rum-bak',
max_count = None

fs = rc._FileSearch(
search_params,
self.get_file_attr('searches.txt'),
file_id,
flags,
context,
encoding,
backup_ext,
max_count
)

for result in fs.run():
if result.error is not None:
print(''.join(result.error))

with codecs.open(self.norm('searches.txt'), 'r', encoding='utf-8') as f:
self.assertEqual(f.read(), after)

def test_regex_chain_replace(self):
"""Test for `regex` search and replace."""

self.mktemp(
'searches.txt',
content=self.dedent(
'''
search1
search1
search2
search2
search3
search3
search1, search2, search3
'''
).encode('utf-8')
)

after = self.dedent(
'''
replace1
replace1
replace2
replace2
search3
search3
replace1, replace2, search3
'''
)

search_params = rc.Search(True)
search_params.add('search1', 'replace1', rc.IGNORECASE)
search_params.add('search2', 'replace2', rc.IGNORECASE)

file_id = 0
encoding = None
context = (0, 0)
flags = 0
backup_ext = 'rum-bak',
max_count = None

fs = rc._FileSearch(
search_params,
self.get_file_attr('searches.txt'),
file_id,
flags,
context,
encoding,
backup_ext,
max_count,
regex_mode=rc.REGEX_MODE
)

for result in fs.run():
if result.error is not None:
print(''.join(result.error))

with codecs.open(self.norm('searches.txt'), 'r', encoding='utf-8') as f:
self.assertEqual(f.read(), after)

def test_literal_chain_replace(self):
"""Test for literal search and replace."""

Expand Down

0 comments on commit 657ae18

Please sign in to comment.