Repository navigation
unicodedata: Fix name, unmask test_name
#8599
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ use icu_properties::props::{ | |
| }; | ||
| use rustpython_wtf8::CodePoint; | ||
|
|
||
| include!(concat!(env!("OUT_DIR"), "/generated/algo_names.rs")); | ||
| include!(concat!(env!("OUT_DIR"), "/generated/bidi_class_3_2.rs")); | ||
| include!(concat!(env!("OUT_DIR"), "/generated/binary_props_3_2.rs")); | ||
| include!(concat!( | ||
|
|
@@ -75,6 +76,37 @@ impl DecompositionType { | |
| } | ||
| } | ||
|
|
||
| #[derive(Clone, Copy, PartialEq)] | ||
| enum AlgorithmicName { | ||
| TangutIdeograph, | ||
| } | ||
|
|
||
| impl AlgorithmicName { | ||
| const fn name_base(self) -> &'static str { | ||
| match self { | ||
| Self::TangutIdeograph => "TANGUT IDEOGRAPH", | ||
| } | ||
| } | ||
|
|
||
| fn to_name(self, ch: char) -> String { | ||
| match self { | ||
| Self::TangutIdeograph => format!("{}-{:0X}", self.name_base(), ch as u32), | ||
| } | ||
| } | ||
|
|
||
| fn check_name(search_name: &str) -> Option<char> { | ||
| if let Some(without_base) = search_name.strip_prefix(Self::TangutIdeograph.name_base()) { | ||
| let cp = u32::from_str_radix(without_base.strip_prefix('-')?.trim(), 16).ok()?; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🌐 Web query:
💡 Result: Yes, the Rust u32::from_str_radix function accepts an optional leading plus sign ('+') followed by digits [1][2][3][4]. The official documentation explicitly states that the string is expected to be an optional '+' sign followed by digits [1][4]. Note that while this behavior is intentional for numeric parsing, it has been noted as a potential pitfall when users attempt to parse hexadecimal strings (where a leading '+' might be unexpected) [5]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target definitions and callers ---'
rg -n -C 8 'lookup_character|from_str_radix|strip_prefix' crates/unicode/src/data.rs crates/codegen/src/string_parser.rs
printf '%s\n' '--- target file context ---'
sed -n '70,115p' crates/unicode/src/data.rs
printf '%s\n' '--- parser context ---'
sed -n '95,130p' crates/codegen/src/string_parser.rsRepository: RustPython/RustPython Length of output: 9101 🤖 get_repo_knowledge executed:
Length of output: 13523 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- algorithmic-name table and all lookup callers ---'
rg -n -C 5 'ALGO_NAMES|lookup_character\(' crates/unicode/src crates/stdlib/src crates/codegen/src
printf '%s\n' '--- Unicode data crate metadata ---'
rg -n -C 3 'unicode_names2|unicode' crates/unicode/Cargo.toml Cargo.tomlRepository: RustPython/RustPython Length of output: 7105 🌐 Web query:
💡 Result: In Rust, the u32::from_str_radix function expects a string slice that consists of an optional plus sign ('+') followed by digits [1][2]. It does not support a negative sign (as it parses to an unsigned integer). The digits accepted depend on the provided radix (2-36), and include both '0'-'9' and 'a'-'z' or 'A'-'Z' for hexadecimal (radix 16) [1][3]. Leading and trailing whitespace in the input string are considered errors [1][4]. Regarding CPython's unicodedata.lookup function and Tangut ideographs: historically, lookup did not support algorithmic names for Tangut ideographs, causing them to raise a ValueError [5][6]. This was addressed in CPython (notably in versions 3.13 and later) to correctly support these names [5][7]. The name format for these ideographs follows the pattern "TANGUT IDEOGRAPH-XXXXX", where "XXXXX" is the hexadecimal representation of the code point [6][8]. The lookup function is case-insensitive for most names, but algorithmic names like those for Tangut ideographs must generally conform to the expected format [5][8]. Whitespace within the name string is not a supported separator for the algorithmic portion (e.g., "TANGUT IDEOGRAPH-17000" is correct, whereas adding arbitrary whitespace inside the identifier component would not be) [8]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- Tangut range definition and lookup contract ---'
rg -n -C 8 'ALGO_NAMES|TangutIdeograph|TANGUT IDEOGRAPH|17000|17002' crates/unicode
printf '%s\n' '--- unicodedata lookup error path ---'
sed -n '88,116p' crates/stdlib/src/unicodedata.rsRepository: RustPython/RustPython Length of output: 50377 Reject non-canonical Tangut name suffixes.
🤖 Prompt for AI Agents |
||
| let ch = char::from_u32(cp)?; | ||
| if lookup_table(ALGO_NAMES, ch)? == Self::TangutIdeograph { | ||
| return Some(ch); | ||
| } | ||
| } | ||
|
|
||
| None | ||
| } | ||
| } | ||
|
|
||
| fn lookup_table<T: Copy>(table: &[(u32, u32, T)], ch: char) -> Option<T> { | ||
| let ch = ch as u32; | ||
| table | ||
|
|
@@ -91,7 +123,10 @@ fn lookup_table<T: Copy>(table: &[(u32, u32, T)], ch: char) -> Option<T> { | |
| .map(|i| table[i].2) | ||
| } | ||
|
|
||
| fn membership_3_2(ch: u32) -> bool { | ||
| #[cold] | ||
| #[inline(never)] | ||
| #[must_use] | ||
| pub fn membership_3_2(ch: u32) -> bool { | ||
| MEMBERSHIP_3_2 | ||
| .binary_search_by(|&(start, end)| { | ||
| if ch > end { | ||
|
|
@@ -136,12 +171,17 @@ pub fn unicode_version() -> String { | |
| } | ||
|
|
||
| /// Look up a character by its Unicode name (`unicodedata.lookup`). | ||
| pub use unicode_names2::character as lookup_character; | ||
| #[must_use] | ||
| pub fn lookup_character(search_name: &str) -> Option<char> { | ||
| unicode_names2::character(search_name).or_else(|| AlgorithmicName::check_name(search_name)) | ||
| } | ||
|
|
||
| /// The Unicode name of `ch` (`unicodedata.name`), if any. | ||
| #[must_use] | ||
| pub fn character_name(ch: char) -> Option<String> { | ||
| unicode_names2::name(ch).map(|name| name.to_string()) | ||
| unicode_names2::name(ch) | ||
| .map(|name| name.to_string()) | ||
| .or_else(|| lookup_table(ALGO_NAMES, ch).map(|v| v.to_name(ch))) | ||
| } | ||
|
|
||
| /// A view over the Unicode character database at a fixed version. | ||
|
|
@@ -159,6 +199,17 @@ impl Ucd { | |
| Self { modern } | ||
| } | ||
|
|
||
| #[must_use] | ||
| pub fn membership(self, ch: char) -> bool { | ||
| if !self.modern { | ||
| cold_path(); | ||
| membership_3_2(ch as u32) | ||
| } else { | ||
| // Rust chars always "exist" for modern Unicode or else they wouldn't be chars. | ||
| true | ||
| } | ||
| } | ||
|
|
||
| #[must_use] | ||
| pub fn category(&self, c: CodePoint) -> &'static str { | ||
| let Some(c) = c.to_char() else { | ||
|
|
||
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 had to remove these lines due to
scripts/check_redundant_patches.py.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.
that's intended!