Stabilize quote-style preserve (#9922)

This commit is contained in:
Micha Reiser 2024-02-12 10:30:07 +01:00 committed by GitHub
parent 6dc1b21917
commit 4946a1876f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 37 additions and 33 deletions

View file

@ -248,6 +248,12 @@ pub enum QuoteStyle {
Preserve, Preserve,
} }
impl QuoteStyle {
pub const fn is_preserve(self) -> bool {
matches!(self, QuoteStyle::Preserve)
}
}
impl fmt::Display for QuoteStyle { impl fmt::Display for QuoteStyle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {

View file

@ -50,11 +50,13 @@ impl Format<PyFormatContext<'_>> for FormatStringLiteral<'_> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
let locator = f.context().locator(); let locator = f.context().locator();
let quote_style = if self.layout.is_docstring() { let quote_style = f.options().quote_style();
// Per PEP 8 and PEP 257, always prefer double quotes for docstrings let quote_style = if self.layout.is_docstring() && !quote_style.is_preserve() {
// Per PEP 8 and PEP 257, always prefer double quotes for docstrings,
// except when using quote-style=preserve
QuoteStyle::Double QuoteStyle::Double
} else { } else {
f.options().quote_style() quote_style
}; };
let normalized = StringPart::from_source(self.value.range(), &locator).normalize( let normalized = StringPart::from_source(self.value.range(), &locator).normalize(

View file

@ -306,6 +306,7 @@ impl StringPart {
normalize_hex: bool, normalize_hex: bool,
) -> NormalizedString<'a> { ) -> NormalizedString<'a> {
// Per PEP 8, always prefer double quotes for triple-quoted strings. // Per PEP 8, always prefer double quotes for triple-quoted strings.
// Except when using quote-style-preserve.
let preferred_style = if self.quotes.triple { let preferred_style = if self.quotes.triple {
// ... unless we're formatting a code snippet inside a docstring, // ... unless we're formatting a code snippet inside a docstring,
// then we specifically want to invert our quote style to avoid // then we specifically want to invert our quote style to avoid
@ -354,6 +355,8 @@ impl StringPart {
// if it doesn't have perfect alignment with PEP8. // if it doesn't have perfect alignment with PEP8.
if let Some(quote) = parent_docstring_quote_char { if let Some(quote) = parent_docstring_quote_char {
QuoteStyle::from(quote.invert()) QuoteStyle::from(quote.invert())
} else if configured_style.is_preserve() {
QuoteStyle::Preserve
} else { } else {
QuoteStyle::Double QuoteStyle::Double
} }

View file

@ -263,21 +263,21 @@ rb"rb double"
rb'br single' rb'br single'
rb"br double" rb"br double"
"""single triple""" '''single triple'''
"""double triple""" """double triple"""
r"""r single triple""" r'''r single triple'''
r"""r double triple""" r"""r double triple"""
f"""f single triple""" f'''f single triple'''
f"""f double triple""" f"""f double triple"""
rf"""fr single triple""" rf'''fr single triple'''
rf"""fr double triple""" rf"""fr double triple"""
rf"""rf single triple""" rf'''rf single triple'''
rf"""rf double triple""" rf"""rf double triple"""
b"""b single triple""" b'''b single triple'''
b"""b double triple""" b"""b double triple"""
rb"""rb single triple""" rb'''rb single triple'''
rb"""rb double triple""" rb"""rb double triple"""
rb"""br single triple""" rb'''br single triple'''
rb"""br double triple""" rb"""br double triple"""
'single1' 'single2' 'single1' 'single2'
@ -287,7 +287,7 @@ rb"""br double triple"""
def docstring_single_triple(): def docstring_single_triple():
"""single triple""" '''single triple'''
def docstring_double_triple(): def docstring_double_triple():
@ -299,7 +299,7 @@ def docstring_double():
def docstring_single(): def docstring_single():
"single" 'single'
``` ```
@ -308,8 +308,7 @@ def docstring_single():
--- Stable --- Stable
+++ Preview +++ Preview
@@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
-'single' # this string is treated as a docstring 'single' # this string is treated as a docstring
+"single" # this string is treated as a docstring
+ +
"double" "double"
r'r single' r'r single'

View file

@ -166,12 +166,6 @@ impl Configuration {
PreviewMode::Enabled => ruff_python_formatter::PreviewMode::Enabled, PreviewMode::Enabled => ruff_python_formatter::PreviewMode::Enabled,
}; };
if quote_style == QuoteStyle::Preserve && !format_preview.is_enabled() {
return Err(anyhow!(
"'quote-style = preserve' is a preview only feature. Run with '--preview' to enable it."
));
}
let formatter = FormatterSettings { let formatter = FormatterSettings {
exclude: FilePatternSet::try_from_iter(format.exclude.unwrap_or_default())?, exclude: FilePatternSet::try_from_iter(format.exclude.unwrap_or_default())?,
extension: self.extension.clone().unwrap_or_default(), extension: self.extension.clone().unwrap_or_default(),

View file

@ -2942,28 +2942,28 @@ pub struct FormatOptions {
)] )]
pub indent_style: Option<IndentStyle>, pub indent_style: Option<IndentStyle>,
/// Configures the preferred quote character for strings. Valid options are: /// Configures the preferred quote character for strings. The recommended options are
///
/// * `double` (default): Use double quotes `"` /// * `double` (default): Use double quotes `"`
/// * `single`: Use single quotes `'` /// * `single`: Use single quotes `'`
/// * `preserve` (preview only): Keeps the existing quote character. We don't recommend using this option except for projects
/// that already use a mixture of single and double quotes and can't migrate to using double or single quotes.
/// ///
/// In compliance with [PEP 8](https://peps.python.org/pep-0008/) and [PEP 257](https://peps.python.org/pep-0257/), /// In compliance with [PEP 8](https://peps.python.org/pep-0008/) and [PEP 257](https://peps.python.org/pep-0257/),
/// Ruff prefers double quotes for multiline strings and docstrings, regardless of the /// Ruff prefers double quotes for triple quoted strings and docstrings even when using `quote-style = "single"`.
/// configured quote style.
/// ///
/// Ruff may also deviate from using the configured quotes if doing so requires /// Ruff deviates from using the configured quotes if doing so prevents the need for
/// escaping quote characters within the string. For example, given: /// escaping quote characters inside the string:
/// ///
/// ```python /// ```python
/// a = "a string without any quotes" /// a = "a string without any quotes"
/// b = "It's monday morning" /// b = "It's monday morning"
/// ``` /// ```
/// ///
/// Ruff will change `a` to use single quotes when using `quote-style = "single"`. However, /// Ruff will change the quotes of the string assigned to `a` to single quotes when using `quote-style = "single"`.
/// `b` remains unchanged, as converting to single quotes requires escaping the inner `'`, /// However, ruff uses double quotes for he string assigned to `b` because using single quotes would require escaping the `'`,
/// which leads to less readable code: `'It\'s monday morning'`. This does not apply when using `preserve`. /// which leads to the less readable code: `'It\'s monday morning'`.
///
/// In addition, Ruff supports the quote style `preserve` for projects that already use
/// a mixture of single and double quotes and can't migrate to the `double` or `single` style.
/// The quote style `preserve` leaves the quotes of all strings unchanged.
#[option( #[option(
default = r#"double"#, default = r#"double"#,
value_type = r#""double" | "single" | "preserve""#, value_type = r#""double" | "single" | "preserve""#,

2
ruff.schema.json generated
View file

@ -1373,7 +1373,7 @@
] ]
}, },
"quote-style": { "quote-style": {
"description": "Configures the preferred quote character for strings. Valid options are:\n\n* `double` (default): Use double quotes `\"` * `single`: Use single quotes `'` * `preserve` (preview only): Keeps the existing quote character. We don't recommend using this option except for projects that already use a mixture of single and double quotes and can't migrate to using double or single quotes.\n\nIn compliance with [PEP 8](https://peps.python.org/pep-0008/) and [PEP 257](https://peps.python.org/pep-0257/), Ruff prefers double quotes for multiline strings and docstrings, regardless of the configured quote style.\n\nRuff may also deviate from using the configured quotes if doing so requires escaping quote characters within the string. For example, given:\n\n```python a = \"a string without any quotes\" b = \"It's monday morning\" ```\n\nRuff will change `a` to use single quotes when using `quote-style = \"single\"`. However, `b` remains unchanged, as converting to single quotes requires escaping the inner `'`, which leads to less readable code: `'It\\'s monday morning'`. This does not apply when using `preserve`.", "description": "Configures the preferred quote character for strings. The recommended options are * `double` (default): Use double quotes `\"` * `single`: Use single quotes `'`\n\nIn compliance with [PEP 8](https://peps.python.org/pep-0008/) and [PEP 257](https://peps.python.org/pep-0257/), Ruff prefers double quotes for triple quoted strings and docstrings even when using `quote-style = \"single\"`.\n\nRuff deviates from using the configured quotes if doing so prevents the need for escaping quote characters inside the string:\n\n```python a = \"a string without any quotes\" b = \"It's monday morning\" ```\n\nRuff will change the quotes of the string assigned to `a` to single quotes when using `quote-style = \"single\"`. However, ruff uses double quotes for he string assigned to `b` because using single quotes would require escaping the `'`, which leads to the less readable code: `'It\\'s monday morning'`.\n\nIn addition, Ruff supports the quote style `preserve` for projects that already use a mixture of single and double quotes and can't migrate to the `double` or `single` style. The quote style `preserve` leaves the quotes of all strings unchanged.",
"anyOf": [ "anyOf": [
{ {
"$ref": "#/definitions/QuoteStyle" "$ref": "#/definitions/QuoteStyle"