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

Implement an iterator for universal newlines #3454

Merged
merged 5 commits into from
Mar 13, 2023

Conversation

charliermarsh
Copy link
Member

Summary

We need to support CR line endings (as opposed to LF and CRLF line endings, which are already supported). They're rare, but they do appear in Python code, and we tend to panic on any file that uses them.

Our Locator abstraction now supports CR line endings. However, Rust's str#lines implementation does not.

This PR adds a UniversalNewlineIterator implementation that respects all of CR, LF, and CRLF line endings, and plugs it into most of the .lines() call sites.

As an alternative design, it could be nice if we could leverage Locator for this. We've already computed all of the line endings, so we could probably iterate much more efficiently?

Test Plan

Largely relying on automated testing, however, also ran over some known failure cases, like #3404.

@charliermarsh charliermarsh marked this pull request as ready for review March 12, 2023 04:40
@github-actions
Copy link
Contributor

github-actions bot commented Mar 12, 2023

✅ ecosystem check detected no changes.


/// Like `str#lines`, but accommodates LF, CRLF, and CR line endings,
/// the latter of which are not supported by `str#lines`.
pub struct UniversalNewlineIterator<'a> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a feeling that I'm breaking some idioms and that this code could be far more efficient, so feel free to give it a harsh review if it deserves one :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it myself and haven't been able to come up with something much more concise. It's surprisingly complicated

This implementation mutates the underlying string slices rather than tracking start and end positions explicitly (similar to Vec::IntoIter that tracks the start and end pointer). I would need to do some profiling to understand if it is faster than using explicit start and end positions. It might not, because writing a string slice is two words (pointer, length).

/// Like `str#lines`, but accommodates LF, CRLF, and CR line endings,
/// the latter of which are not supported by `str#lines`.
pub struct UniversalNewlineIterator<'a> {
    text: &'a str,
}

impl<'a> UniversalNewlineIterator<'a> {
    pub fn from(text: &'a str) -> UniversalNewlineIterator<'a> {
        UniversalNewlineIterator { text }
    }
}

impl<'a> Iterator for UniversalNewlineIterator<'a> {
    type Item = &'a str;

    #[inline]
    fn next(&mut self) -> Option<&'a str> {
        if self.text.is_empty() {
            return None;
        }

        let line = match self.text.find(['\n', '\r']) {
            // Not the last line
            Some(line_end) => {
                let (line, remainder) = self.text.split_at(line_end);

                self.text = match remainder.as_bytes()[0] {
                    // Explicit branch for `\n` as this is the most likely path
                    b'\n' => &remainder[1..],
                    // '\r\n'
                    b'\r' if remainder.as_bytes().get(1) == Some(&b'\n') => &remainder[2..],
                    // '\r'
                    _ => &remainder[1..],
                };

                line
            }
            // Last line
            None => std::mem::replace(&mut self.text, ""),
        };

        Some(line)
    }
}

impl<'a> DoubleEndedIterator for UniversalNewlineIterator<'_> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.text.is_empty() {
            return None;
        }

        let len = self.text.len();

        // Trim any trailing newlines.
        self.text = match self.text.as_bytes()[len - 1] {
            b'\n' if len > 1 && self.text.as_bytes()[len - 2] == b'\r' => &self.text[..len - 2],
            b'\n' | b'\r' => &self.text[..len - 1],
            _ => &self.text,
        };

        // Find the end of the previous line. The previous line is the text up to, but not including the new line character
        let line = match self.text.rfind(['\n', '\r']) {
            // '\n' or '\r' or '\r\n'
            Some(line_end) => {
                let (remainder, line) = self.text.split_at(line_end + 1);
                self.text = remainder;

                line
            }
            // Last line
            None => std::mem::replace(&mut self.text, ""),
        };

        Some(line)
    }
}

impl FusedIterator for UniversalNewlineIterator<'_> {}

#[cfg(test)]
mod tests {
    use super::UniversalNewlineIterator;

    #[test]
    fn universal_newlines_empty_str() {
        let lines: Vec<_> = UniversalNewlineIterator::from("").collect();
        assert_eq!(lines, Vec::<&str>::default());

        let lines: Vec<_> = UniversalNewlineIterator::from("").rev().collect();
        assert_eq!(lines, Vec::<&str>::default());
    }

    #[test]
    fn universal_newlines_forward() {
        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop").collect();
        assert_eq!(lines, vec!["foo", "bar", "", "baz", "bop"]);

        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop\n").collect();
        assert_eq!(lines, vec!["foo", "bar", "", "baz", "bop"]);

        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop\n\n").collect();
        assert_eq!(lines, vec!["foo", "bar", "", "baz", "bop", ""]);
    }

    #[test]
    fn universal_newlines_backwards() {
        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop")
            .rev()
            .collect();
        assert_eq!(lines, vec!["bop", "baz", "", "bar", "foo"]);

        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\nbaz\rbop\n")
            .rev()
            .collect();

        assert_eq!(lines, vec!["bop", "baz", "", "bar", "foo"]);
    }

    #[test]
    fn universal_newlines_mixed() {
        let mut lines = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop");

        assert_eq!(lines.next_back(), Some("bop"));
        assert_eq!(lines.next(), Some("foo"));
        assert_eq!(lines.next_back(), Some("baz"));
        assert_eq!(lines.next(), Some("bar"));
        assert_eq!(lines.next_back(), Some(""));
        assert_eq!(lines.next(), None);
    }
}

I also added a few more tests.

This is a more "traditional" implementation that uses start and end offsets (similar to how SplitIterator works)

/// Like `str#lines`, but accommodates LF, CRLF, and CR line endings,
/// the latter of which are not supported by `str#lines`.
pub struct UniversalNewlineIterator<'a> {
    text: &'a str,
    start_offset: usize,
    end_offset: usize,
}

impl<'a> UniversalNewlineIterator<'a> {
    pub fn from(text: &'a str) -> UniversalNewlineIterator<'a> {
        UniversalNewlineIterator {
            text,
            start_offset: 0,
            end_offset: text.len(),
        }
    }

    fn as_str(&self) -> &'a str {
        // SAFETY: It's guaranteed that start and end offsets are always positioned at character boundaries.
        unsafe { &self.text.get_unchecked(self.start_offset..self.end_offset) }
    }
}

impl<'a> Iterator for UniversalNewlineIterator<'a> {
    type Item = &'a str;

    #[inline]
    fn next(&mut self) -> Option<&'a str> {
        let text = self.as_str();

        if text.is_empty() {
            return None;
        }

        let line = match text.find(['\n', '\r']) {
            // Not the last line
            Some(line_end) => {
                self.start_offset += line_end
                    + match text.as_bytes()[line_end] {
                        // Explicit branch for `\n` as this is the most likely path
                        b'\n' => 1,
                        // '\r\n'
                        b'\r' if text.as_bytes().get(line_end + 1) == Some(&b'\n') => 2,
                        // '\r'
                        _ => 1,
                    };

                &text[..line_end]
            }
            // Last line
            None => {
                self.start_offset = self.end_offset;
                text
            }
        };

        Some(line)
    }
}

impl<'a> DoubleEndedIterator for UniversalNewlineIterator<'_> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        let mut text = self.as_str();

        if text.is_empty() {
            return None;
        }

        let len = text.len();

        // Trim any trailing newlines.
        text = match text.as_bytes()[len - 1] {
            b'\n' if len > 1 && text.as_bytes()[len - 2] == b'\r' => &text[..len - 2],
            b'\n' | b'\r' => &text[..len - 1],
            _ => &text,
        };

        // Find the end of the previous line. The previous line is the text up to, but not including the new line character
        let line = match text.rfind(['\n', '\r']) {
            // '\n' or '\r' or '\r\n'
            Some(newline_pos) => {
                let line_start = newline_pos + 1;
                let line = &text[line_start..];
                self.end_offset = self.start_offset + line_start;

                line
            }
            // Last line
            None => {
                self.end_offset = self.start_offset;
                text
            }
        };

        Some(line)
    }
}

impl FusedIterator for UniversalNewlineIterator<'_> {}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took the first suggestion, since the latter requires unsafe, and it'd be our only unsafe block right now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I did some cursory benchmarking, and what you had here minorly outperformed the current version in the PR -- but I'd need to run an actual microbenchmark.)

Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an issue with the iterator implementation that it allows next_back and next to pass each other, which isn't allowed according to the Iterator protocol. Other than that, looks good to me (I proposed an alternative and suggested some more tests but it leads to about the same amount of code)

It would be good to add a few tests we knew were failing before to verify that the new logic handles \r correctly.

I'm undecided if we should create an extension trait to implement universal_lines on &str. It could help discoverability (over UniversalNewlinesIterator::from(str).

crates/ruff_python_ast/src/whitespace.rs Outdated Show resolved Hide resolved
crates/ruff_python_ast/src/whitespace.rs Outdated Show resolved Hide resolved
crates/ruff_python_ast/src/whitespace.rs Outdated Show resolved Hide resolved
crates/ruff_python_ast/src/whitespace.rs Outdated Show resolved Hide resolved

/// Like `str#lines`, but accommodates LF, CRLF, and CR line endings,
/// the latter of which are not supported by `str#lines`.
pub struct UniversalNewlineIterator<'a> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it myself and haven't been able to come up with something much more concise. It's surprisingly complicated

This implementation mutates the underlying string slices rather than tracking start and end positions explicitly (similar to Vec::IntoIter that tracks the start and end pointer). I would need to do some profiling to understand if it is faster than using explicit start and end positions. It might not, because writing a string slice is two words (pointer, length).

/// Like `str#lines`, but accommodates LF, CRLF, and CR line endings,
/// the latter of which are not supported by `str#lines`.
pub struct UniversalNewlineIterator<'a> {
    text: &'a str,
}

impl<'a> UniversalNewlineIterator<'a> {
    pub fn from(text: &'a str) -> UniversalNewlineIterator<'a> {
        UniversalNewlineIterator { text }
    }
}

impl<'a> Iterator for UniversalNewlineIterator<'a> {
    type Item = &'a str;

    #[inline]
    fn next(&mut self) -> Option<&'a str> {
        if self.text.is_empty() {
            return None;
        }

        let line = match self.text.find(['\n', '\r']) {
            // Not the last line
            Some(line_end) => {
                let (line, remainder) = self.text.split_at(line_end);

                self.text = match remainder.as_bytes()[0] {
                    // Explicit branch for `\n` as this is the most likely path
                    b'\n' => &remainder[1..],
                    // '\r\n'
                    b'\r' if remainder.as_bytes().get(1) == Some(&b'\n') => &remainder[2..],
                    // '\r'
                    _ => &remainder[1..],
                };

                line
            }
            // Last line
            None => std::mem::replace(&mut self.text, ""),
        };

        Some(line)
    }
}

impl<'a> DoubleEndedIterator for UniversalNewlineIterator<'_> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.text.is_empty() {
            return None;
        }

        let len = self.text.len();

        // Trim any trailing newlines.
        self.text = match self.text.as_bytes()[len - 1] {
            b'\n' if len > 1 && self.text.as_bytes()[len - 2] == b'\r' => &self.text[..len - 2],
            b'\n' | b'\r' => &self.text[..len - 1],
            _ => &self.text,
        };

        // Find the end of the previous line. The previous line is the text up to, but not including the new line character
        let line = match self.text.rfind(['\n', '\r']) {
            // '\n' or '\r' or '\r\n'
            Some(line_end) => {
                let (remainder, line) = self.text.split_at(line_end + 1);
                self.text = remainder;

                line
            }
            // Last line
            None => std::mem::replace(&mut self.text, ""),
        };

        Some(line)
    }
}

impl FusedIterator for UniversalNewlineIterator<'_> {}

#[cfg(test)]
mod tests {
    use super::UniversalNewlineIterator;

    #[test]
    fn universal_newlines_empty_str() {
        let lines: Vec<_> = UniversalNewlineIterator::from("").collect();
        assert_eq!(lines, Vec::<&str>::default());

        let lines: Vec<_> = UniversalNewlineIterator::from("").rev().collect();
        assert_eq!(lines, Vec::<&str>::default());
    }

    #[test]
    fn universal_newlines_forward() {
        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop").collect();
        assert_eq!(lines, vec!["foo", "bar", "", "baz", "bop"]);

        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop\n").collect();
        assert_eq!(lines, vec!["foo", "bar", "", "baz", "bop"]);

        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop\n\n").collect();
        assert_eq!(lines, vec!["foo", "bar", "", "baz", "bop", ""]);
    }

    #[test]
    fn universal_newlines_backwards() {
        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop")
            .rev()
            .collect();
        assert_eq!(lines, vec!["bop", "baz", "", "bar", "foo"]);

        let lines: Vec<_> = UniversalNewlineIterator::from("foo\nbar\n\nbaz\rbop\n")
            .rev()
            .collect();

        assert_eq!(lines, vec!["bop", "baz", "", "bar", "foo"]);
    }

    #[test]
    fn universal_newlines_mixed() {
        let mut lines = UniversalNewlineIterator::from("foo\nbar\n\r\nbaz\rbop");

        assert_eq!(lines.next_back(), Some("bop"));
        assert_eq!(lines.next(), Some("foo"));
        assert_eq!(lines.next_back(), Some("baz"));
        assert_eq!(lines.next(), Some("bar"));
        assert_eq!(lines.next_back(), Some(""));
        assert_eq!(lines.next(), None);
    }
}

I also added a few more tests.

This is a more "traditional" implementation that uses start and end offsets (similar to how SplitIterator works)

/// Like `str#lines`, but accommodates LF, CRLF, and CR line endings,
/// the latter of which are not supported by `str#lines`.
pub struct UniversalNewlineIterator<'a> {
    text: &'a str,
    start_offset: usize,
    end_offset: usize,
}

impl<'a> UniversalNewlineIterator<'a> {
    pub fn from(text: &'a str) -> UniversalNewlineIterator<'a> {
        UniversalNewlineIterator {
            text,
            start_offset: 0,
            end_offset: text.len(),
        }
    }

    fn as_str(&self) -> &'a str {
        // SAFETY: It's guaranteed that start and end offsets are always positioned at character boundaries.
        unsafe { &self.text.get_unchecked(self.start_offset..self.end_offset) }
    }
}

impl<'a> Iterator for UniversalNewlineIterator<'a> {
    type Item = &'a str;

    #[inline]
    fn next(&mut self) -> Option<&'a str> {
        let text = self.as_str();

        if text.is_empty() {
            return None;
        }

        let line = match text.find(['\n', '\r']) {
            // Not the last line
            Some(line_end) => {
                self.start_offset += line_end
                    + match text.as_bytes()[line_end] {
                        // Explicit branch for `\n` as this is the most likely path
                        b'\n' => 1,
                        // '\r\n'
                        b'\r' if text.as_bytes().get(line_end + 1) == Some(&b'\n') => 2,
                        // '\r'
                        _ => 1,
                    };

                &text[..line_end]
            }
            // Last line
            None => {
                self.start_offset = self.end_offset;
                text
            }
        };

        Some(line)
    }
}

impl<'a> DoubleEndedIterator for UniversalNewlineIterator<'_> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        let mut text = self.as_str();

        if text.is_empty() {
            return None;
        }

        let len = text.len();

        // Trim any trailing newlines.
        text = match text.as_bytes()[len - 1] {
            b'\n' if len > 1 && text.as_bytes()[len - 2] == b'\r' => &text[..len - 2],
            b'\n' | b'\r' => &text[..len - 1],
            _ => &text,
        };

        // Find the end of the previous line. The previous line is the text up to, but not including the new line character
        let line = match text.rfind(['\n', '\r']) {
            // '\n' or '\r' or '\r\n'
            Some(newline_pos) => {
                let line_start = newline_pos + 1;
                let line = &text[line_start..];
                self.end_offset = self.start_offset + line_start;

                line
            }
            // Last line
            None => {
                self.end_offset = self.start_offset;
                text
            }
        };

        Some(line)
    }
}

impl FusedIterator for UniversalNewlineIterator<'_> {}

crates/ruff_python_ast/src/whitespace.rs Outdated Show resolved Hide resolved
@@ -24,4 +24,5 @@ regex = { workspace = true }
rustc-hash = { workspace = true }
rustpython-common = { workspace = true }
rustpython-parser = { workspace = true }
serde = { workspace = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we have to add the serde dependency?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I should've documented this. I think it's an issue with how I added the serde feature in RustPython. It looks like it doesn't have the derive feature enabled. So we need to use "our" serde, which does have derive, in order for it to compile. (This is based on cursory guess, I didn't look deeply, but I'll create an issue and document.)

crates/ruff/src/rules/pydocstyle/rules/sections.rs Outdated Show resolved Hide resolved
@charliermarsh charliermarsh force-pushed the charlie/universal-newlines branch 2 times, most recently from 81d3ea2 to ff80e38 Compare March 13, 2023 03:43
/// Extension trait for [`str`] that provides a [`UniversalNewlineIterator`].
pub trait StrExt {
fn universal_newlines(&self) -> UniversalNewlineIterator<'_>;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and added an extension trait, but if you have any feedback @MichaReiser happy to address in a follow-up.

@charliermarsh charliermarsh merged commit c2750a5 into main Mar 13, 2023
@charliermarsh charliermarsh deleted the charlie/universal-newlines branch March 13, 2023 04:01
renovate bot added a commit to ixm-one/pytest-cmake-presets that referenced this pull request Mar 14, 2023
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [ruff](https://togithub.com/charliermarsh/ruff) | `^0.0.254` ->
`^0.0.255` |
[![age](https://badges.renovateapi.com/packages/pypi/ruff/0.0.255/age-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://badges.renovateapi.com/packages/pypi/ruff/0.0.255/adoption-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://badges.renovateapi.com/packages/pypi/ruff/0.0.255/compatibility-slim/0.0.254)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://badges.renovateapi.com/packages/pypi/ruff/0.0.255/confidence-slim/0.0.254)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>charliermarsh/ruff</summary>

###
[`v0.0.255`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.255)

[Compare
Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.254...v0.0.255)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

##### Rules

- \[`flake8-pie`] Fix PIE802 broken auto-fix with trailing comma by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3402
- \[`flake8-pie`] Implement autofix for PIE810 by
[@&#8203;kyoto7250](https://togithub.com/kyoto7250) in
[astral-sh/ruff#3411
- \[`flake8-bugbear`] Add `flake8-bugbear`'s B030 rule by
[@&#8203;aacunningham](https://togithub.com/aacunningham) in
[astral-sh/ruff#3400
- \[`pycodestyle`] Add E231 by
[@&#8203;carlosmiei](https://togithub.com/carlosmiei) in
[astral-sh/ruff#3344
- \[`pyupgrade`] Flag deprecated (but renamed) imports in UP035 by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3448
- \[`pyupgrade`] Remap ChainMap, Counter, and OrderedDict imports to
collections by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3392
- \[`pylint`] C1901: compare-to-empty-string by
[@&#8203;AreamanM](https://togithub.com/AreamanM) in
[astral-sh/ruff#3405
- \[`pylint`] Implement W1508 invalid-envvar-default by
[@&#8203;latonis](https://togithub.com/latonis) in
[astral-sh/ruff#3449
- \[`pylint`] Implement E1507 invalid-envvar-value by
[@&#8203;latonis](https://togithub.com/latonis) in
[astral-sh/ruff#3467

##### Settings

- Infer `target-version` from project metadata by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3470
- Implement configuration options `runtime-evaluated-decorators` and
`runtime-evaluated-base-classes` for `flake8-type-checking` by
[@&#8203;sasanjac](https://togithub.com/sasanjac) in
[astral-sh/ruff#3292
- Add Azure DevOps as a `--format` option. by
[@&#8203;StefanBRas](https://togithub.com/StefanBRas) in
[astral-sh/ruff#3335

##### Bug Fixes

- Re-enable the T and C linter prefix selectors by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3452
- Treat unary operations on constants as constant-like by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3348
- Skip byte-order-mark at start of file by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3343
- Don't enforce typing-import rules in .pyi files by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3362
- Include entire prefix when reporting rule selector errors by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3375
- Handle multi-line fixes for byte-string prefixing by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3391
- Catch RET504 usages via decorators by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3395
- Ignore multiply-assigned variables in RET504 by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3393
- \[FIX] PYI011: recognize `Bool` / `Float` / `Complex` numbers as
simple defaults by [@&#8203;XuehaiPan](https://togithub.com/XuehaiPan)
in
[astral-sh/ruff#3459
- Respect ignores for runtime-import-in-type-checking-block (TCH004) by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3474
- Avoid panicking in invalid_escape_sequence by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3338
- fix: Emit a more useful error if an `extend` points at a non-existent
ruff.toml file. by [@&#8203;DanCardin](https://togithub.com/DanCardin)
in
[astral-sh/ruff#3417
- Respect `--show-fixes` with `--fix-only` by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3426
- When "Args" and "Parameters" are present, prefer NumPy style by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3430
- Remove empty line after RUF100 auto-fix by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3414
- Avoid removing un-aliased exceptions in `OSError`-aliased handlers by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3451
- Use a hash to fingerprint GitLab CI output by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3456
- Avoid respecting noqa directives when RUF100 is enabled by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3469
- Output GitLab paths relative to `CI_PROJECT_DIR` by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3475
- Implement an iterator for universal newlines by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3454

#### New Contributors

- [@&#8203;sasanjac](https://togithub.com/sasanjac) made their first
contribution in
[astral-sh/ruff#3292
- [@&#8203;aacunningham](https://togithub.com/aacunningham) made their
first contribution in
[astral-sh/ruff#3380
- [@&#8203;orf](https://togithub.com/orf) made their first contribution
in
[astral-sh/ruff#3389
- [@&#8203;DanCardin](https://togithub.com/DanCardin) made their first
contribution in
[astral-sh/ruff#3417
- [@&#8203;AreamanM](https://togithub.com/AreamanM) made their first
contribution in
[astral-sh/ruff#3405
- [@&#8203;kyoto7250](https://togithub.com/kyoto7250) made their first
contribution in
[astral-sh/ruff#3411
- [@&#8203;latonis](https://togithub.com/latonis) made their first
contribution in
[astral-sh/ruff#3449
- [@&#8203;XuehaiPan](https://togithub.com/XuehaiPan) made their first
contribution in
[astral-sh/ruff#3459
- [@&#8203;CalumY](https://togithub.com/CalumY) made their first
contribution in
[astral-sh/ruff#3461
- [@&#8203;YDX-2147483647](https://togithub.com/YDX-2147483647) made
their first contribution in
[astral-sh/ruff#3473

**Full Changelog**:
astral-sh/ruff@v0.0.254...v0.0.255

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://app.renovatebot.com/dashboard#github/ixm-one/pytest-cmake-presets).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xNjAuMCIsInVwZGF0ZWRJblZlciI6IjM0LjE2MC4wIn0=-->

Signed-off-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
renovate bot added a commit to allenporter/flux-local that referenced this pull request Mar 15, 2023
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [ruff](https://togithub.com/charliermarsh/ruff) | `==0.0.254` ->
`==0.0.256` |
[![age](https://badges.renovateapi.com/packages/pypi/ruff/0.0.256/age-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://badges.renovateapi.com/packages/pypi/ruff/0.0.256/adoption-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://badges.renovateapi.com/packages/pypi/ruff/0.0.256/compatibility-slim/0.0.254)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://badges.renovateapi.com/packages/pypi/ruff/0.0.256/confidence-slim/0.0.254)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>charliermarsh/ruff</summary>

###
[`v0.0.256`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.256)

[Compare
Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.255...v0.0.256)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

##### Bug Fixes

- PYI011: allow `math` constants in defaults by
[@&#8203;XuehaiPan](https://togithub.com/XuehaiPan) in
[astral-sh/ruff#3484
- Remove erroneous C4-to-C40 redirect by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3488
- fix lack of `not` in error message by
[@&#8203;Czaki](https://togithub.com/Czaki) in
[astral-sh/ruff#3497
- Ensure that redirect warnings appear exactly once per code by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3500
- Allow f-strings and concatenations in os.getenv by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3516
- Allow string percent formatting in os.getenv by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3518
- Refine complexity rules for try-except-else-finally by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3519
- Replicate inline comments when splitting single-line imports by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3521
- Avoid PEP 604 isinstance errors for starred tuples by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3527
- Avoid tracking as-imports separately with force-single-line by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3530
- Fix PYI011 and add auto-fix by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3492
- Avoid PEP 604 panic with empty tuple by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3526
- Add last remaining deprecated typing imports by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3529
- Avoid unused argument violations in .pyi files by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3533

##### Other Changes

- Include individual path checks in --verbose logging by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3489
- Allow `# ruff:` prefix for isort action comments by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3493

**Full Changelog**:
astral-sh/ruff@v0.0.255...v0.0.256

###
[`v0.0.255`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.255)

[Compare
Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.254...v0.0.255)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

##### Rules

- \[`flake8-pie`] Fix PIE802 broken auto-fix with trailing comma by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3402
- \[`flake8-pie`] Implement autofix for PIE810 by
[@&#8203;kyoto7250](https://togithub.com/kyoto7250) in
[astral-sh/ruff#3411
- \[`flake8-bugbear`] Add `flake8-bugbear`'s B030 rule by
[@&#8203;aacunningham](https://togithub.com/aacunningham) in
[astral-sh/ruff#3400
- \[`pycodestyle`] Add E231 by
[@&#8203;carlosmiei](https://togithub.com/carlosmiei) in
[astral-sh/ruff#3344
- \[`pyupgrade`] Flag deprecated (but renamed) imports in UP035 by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3448
- \[`pyupgrade`] Remap ChainMap, Counter, and OrderedDict imports to
collections by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3392
- \[`pylint`] C1901: compare-to-empty-string by
[@&#8203;AreamanM](https://togithub.com/AreamanM) in
[astral-sh/ruff#3405
- \[`pylint`] Implement W1508 invalid-envvar-default by
[@&#8203;latonis](https://togithub.com/latonis) in
[astral-sh/ruff#3449
- \[`pylint`] Implement E1507 invalid-envvar-value by
[@&#8203;latonis](https://togithub.com/latonis) in
[astral-sh/ruff#3467

##### Settings

- Infer `target-version` from project metadata by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3470
- Implement configuration options `runtime-evaluated-decorators` and
`runtime-evaluated-base-classes` for `flake8-type-checking` by
[@&#8203;sasanjac](https://togithub.com/sasanjac) in
[astral-sh/ruff#3292
- Add Azure DevOps as a `--format` option. by
[@&#8203;StefanBRas](https://togithub.com/StefanBRas) in
[astral-sh/ruff#3335

##### Bug Fixes

- Re-enable the T and C linter prefix selectors by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3452
- Treat unary operations on constants as constant-like by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3348
- Skip byte-order-mark at start of file by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3343
- Don't enforce typing-import rules in .pyi files by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3362
- Include entire prefix when reporting rule selector errors by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3375
- Handle multi-line fixes for byte-string prefixing by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3391
- Catch RET504 usages via decorators by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3395
- Ignore multiply-assigned variables in RET504 by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3393
- \[FIX] PYI011: recognize `Bool` / `Float` / `Complex` numbers as
simple defaults by [@&#8203;XuehaiPan](https://togithub.com/XuehaiPan)
in
[astral-sh/ruff#3459
- Respect ignores for runtime-import-in-type-checking-block (TCH004) by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3474
- Avoid panicking in invalid_escape_sequence by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3338
- fix: Emit a more useful error if an `extend` points at a non-existent
ruff.toml file. by [@&#8203;DanCardin](https://togithub.com/DanCardin)
in
[astral-sh/ruff#3417
- Respect `--show-fixes` with `--fix-only` by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3426
- When "Args" and "Parameters" are present, prefer NumPy style by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3430
- Remove empty line after RUF100 auto-fix by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3414
- Avoid removing un-aliased exceptions in `OSError`-aliased handlers by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3451
- Use a hash to fingerprint GitLab CI output by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3456
- Avoid respecting noqa directives when RUF100 is enabled by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3469
- Output GitLab paths relative to `CI_PROJECT_DIR` by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3475
- Implement an iterator for universal newlines by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3454

#### New Contributors

- [@&#8203;sasanjac](https://togithub.com/sasanjac) made their first
contribution in
[astral-sh/ruff#3292
- [@&#8203;aacunningham](https://togithub.com/aacunningham) made their
first contribution in
[astral-sh/ruff#3380
- [@&#8203;orf](https://togithub.com/orf) made their first contribution
in
[astral-sh/ruff#3389
- [@&#8203;DanCardin](https://togithub.com/DanCardin) made their first
contribution in
[astral-sh/ruff#3417
- [@&#8203;AreamanM](https://togithub.com/AreamanM) made their first
contribution in
[astral-sh/ruff#3405
- [@&#8203;kyoto7250](https://togithub.com/kyoto7250) made their first
contribution in
[astral-sh/ruff#3411
- [@&#8203;latonis](https://togithub.com/latonis) made their first
contribution in
[astral-sh/ruff#3449
- [@&#8203;XuehaiPan](https://togithub.com/XuehaiPan) made their first
contribution in
[astral-sh/ruff#3459
- [@&#8203;CalumY](https://togithub.com/CalumY) made their first
contribution in
[astral-sh/ruff#3461
- [@&#8203;YDX-2147483647](https://togithub.com/YDX-2147483647) made
their first contribution in
[astral-sh/ruff#3473

**Full Changelog**:
astral-sh/ruff@v0.0.254...v0.0.255

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://app.renovatebot.com/dashboard#github/allenporter/flux-local).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xNjAuMCIsInVwZGF0ZWRJblZlciI6IjM0LjE2MC4wIn0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants