-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
make replace
with count=0 a no-op
#22325
Conversation
base/strings/util.jl
Outdated
@@ -401,7 +402,7 @@ If `pat` is a regular expression and `r` is a `SubstitutionString`, then capture | |||
references in `r` are replaced with the corresponding matched text. | |||
""" | |||
replace(s::AbstractString, pat, f, n::Integer) = replace(String(s), pat, f, n) | |||
replace(s::AbstractString, pat, r) = replace(s, pat, r, 0) | |||
replace(s::AbstractString, pat, r) = replace(s, pat, r, -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably mention this in the docstring
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok (Note that it's not mentioned in Python's docstring either)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is python relevant? The docstring above says "If n
is provided, replace at most n
occurrences." So if there's a special meaning to negative inputs, best to describe that. We don't do as many sign-based behavior changes as Python does, like negative indexing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, that was not so relevant, only as a mature language which doesn't mention the behavior for negative inputs, which is same as here, i.e. replace all. Will add that.
I realize that the default value of |
i like to suggest anther behavior. |
@DrKrar that could be a good idea, except that I prefer that |
base/strings/util.jl
Outdated
@@ -359,6 +359,7 @@ _replace(io, repl::Function, str, r, pattern) = | |||
print(io, repl(SubString(str, first(r), last(r)))) | |||
|
|||
function replace(str::String, pattern, repl, limit::Integer) | |||
limit == 0 && return str |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While you're at it, maybe rename the argument so that it's consistent with the docstring (or change the docstring)?
Actually, I wonder whether we couldn't use |
I really like the idea of |
I added a deprecation warning when count is negative (seems more safe) and a NEW entry. |
I don't think it's worth all the additional complexity of the deprecation, especially because it involves adding lots of code outside of |
I was unprecise, the deprecation I added concerns also |
base/strings/util.jl
Outdated
@@ -358,7 +358,10 @@ _replace(io, repl, str, r, pattern) = print(io, repl) | |||
_replace(io, repl::Function, str, r, pattern) = | |||
print(io, repl(SubString(str, first(r), last(r)))) | |||
|
|||
function replace(str::String, pattern, repl, limit::Integer) | |||
function replace_new(str::String, pattern, repl, count::Int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why restrict this to Int
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's what's done in other contexts too: accept Integer
at the API level, but convert to Int
in the method which does the work, a readons could be to limit the number of compiled functions. But I can see that there is no reason to error out if e.g. a very big BigInt
is passed. I could use clamp(n, 0, typemax(Int)
instead of Int(n)
in the caller (which is in deprecated now), assuming it's never necessary to replace more than typemax(Int)
times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that once the deprecation is removed, the "API level" will be this function, and only Int
will be accepted... So if you want this you need to add another layer which will remain. But really I'm not sure it's worth it, e.g. search
doesn't use that trick.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see. But this replace_new
also only accepts a String
, not an AbstractString
, so this is the replace(s::AbstractString, pat, f) = replace_new(String(s), pat, f, typemax(Int))
(still in strings/util.jl, below replace_new
) which will have to be modified to
replace(s::AbstractString, pat, f, count::Integer=typemax(Int)) = replace(String(s), pat, f, clamp(count, typemin(Int), typemax(Int)) % Int)
.
base/deprecated.jl
Outdated
@@ -1417,6 +1417,20 @@ function LibGit2.set_remote_url(path::AbstractString, url::AbstractString; remot | |||
LibGit2.set_remote_url(path, remote, url) | |||
end | |||
|
|||
function replace(s::AbstractString, pat, f, n::Integer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to add a comment explaining that replace_new
needs to be renamed to replace
when removing this deprecation (and make sure tests will fail if that's not the case).
base/strings/util.jl
Outdated
replace(s::AbstractString, pat, f, n::Integer) = replace(String(s), pat, f, n) | ||
replace(s::AbstractString, pat, r) = replace(s, pat, r, 0) | ||
replace(s::AbstractString, pat, f) = replace_new(String(s), pat, f, typemax(Int)) | ||
# change this to the following when `replace` is removed from deprecated: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with this kind of comment is that nobody is going to notice it when removing deprecation. I'd say let's go with ::Integer
, and once the deprecation has been removed, feel free to open a PR to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hasn't been addressed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that's the second time since yesterday that my comment is not sent, with a "You can't perform this action at this time" message. My last unsent message was:
I added
GenericString
to the tests ofreplace
, so if the deprecation is removed without taking this comment into account, the tests will fail. What I can also do is to open a PR to remove the deprecation right after we merge this.
IOW, the comments cannot be silently ignored and it would be wrong to ignore it even if there was not this conversion-to-Int
change (and I can open the subsequent PR in advance for the next release), but I can update if you still prefer that I postpone this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I'd rather leave this out, but let's hear what others think about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I left it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put a TODO note in base/deprecated.jl
referring to this and saying that it should be removed. That way there's a trail of breadcrumbs when doing the normal deprecations deletion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was already instructions in "base/deprecated.jl", but I updated to be even more clear.
base/deprecated.jl
Outdated
depwarn(string("`replace(s, pat, r, count)` with `count <= 0` is deprecated, use ", | ||
"`replace(s, pat, r, typemax(Int))` or replace(s, pat, r)` instead"), | ||
:replace) | ||
Base.replace(s, pat, f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Base.
not needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok removed.
base/deprecated.jl
Outdated
function replace(s::AbstractString, pat, f, n::Integer) | ||
if n <= 0 | ||
depwarn(string("`replace(s, pat, r, count)` with `count <= 0` is deprecated, use ", | ||
"`replace(s, pat, r, typemax(Int))` or replace(s, pat, r)` instead"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing opening backtick.
base/strings/util.jl
Outdated
replace(s::AbstractString, pat, f, n::Integer) = replace(String(s), pat, f, n) | ||
replace(s::AbstractString, pat, r) = replace(s, pat, r, 0) | ||
replace(s::AbstractString, pat, f) = replace_new(String(s), pat, f, typemax(Int)) | ||
# change this to the following when `replace` is removed from deprecated: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hasn't been addressed.
Looks good to me, modulo this discussion on which I'd like more opinions. |
base/deprecated.jl
Outdated
:replace) | ||
replace(s, pat, f) | ||
else | ||
Base.replace_new(String(s), pat, f, clamp(n, 0, typemax(Int)) % Int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Base.
shouldn't be needed here either, deprecated.jl is included within the Base module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry I should have checked
6e12155
to
d271b78
Compare
Rebased, I will merge in a few days if no objection. |
If we go with a keyword argument in #22324, should we also do the same here? A deprecation is painful enough that we don't want to repeat this process later. Would need to check whether the keyword argument would hurt performance. |
I would say yes we should. But I don't see how to avoid performances loss with a keyword (when the keyword is not used, performance is not hurt, and using the keyword costs about 90ns on my machine, which represents about 50% overhead for a string of length 10). If keyword uses are really expected to become faster, then a temporary slow down may be acceptable (using the |
OK, let's go with this then, a 50% overhead is clearly too much. |
What about for non-string arguments then? would you say the slowness of using keywords is more acceptable because at least it's not a regression (as it didn't exist before)? I feel that if the API for replace on non-string is decided with pairs and keywords (as it's now in #22324), then eventually the string version should accept a similar API. |
It's hard to tell, but I would think that strings are generally smaller than arrays, so the overhead is worse. Also there's a real advantage to using a keyword argument for the array case (due to the pair API), but not really for strings. |
I was thinking of adding the pair API to strings too! |
Why not, that's good for consistency, but we can keep the existing one if it's more efficient. |
d271b78
to
4405d63
Compare
base/strings/util.jl
Outdated
provided, replace at most `n` occurrences. As with search, the second argument may be a | ||
Search for the given pattern `pat` in `s`, and replace each occurrence with `r`. | ||
If `count` is provided, replace at most `count` occurrences. | ||
As with search, the second argument may be a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
search would be good as a cross reference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
CI failure seems unrelated: it concerns libgit2, and a similar problem appears in other unrelated PRs. |
replace
with count=0 a no-op
Before we had
replace("aaa", 'a', 'z', 0) == "zzz"
which can seem unintuitive. This PR changes this to a no-op, while still allowing a negative count to mean unlimited replacements.