88using System ;
99using System . Collections ;
1010using System . Collections . Generic ;
11+ using System . Diagnostics . CodeAnalysis ;
1112using System . IO ;
1213using System . IO . Compression ;
1314using System . Linq ;
1819using IronPython . Runtime ;
1920using IronPython . Runtime . Operations ;
2021
21- [ assembly : PythonModule ( "unicodedata" , typeof ( IronPython . Modules . unicodedata ) ) ]
22+ using NotNullAttribute = Microsoft . Scripting . Runtime . NotNullAttribute ;
2223
24+ [ assembly: PythonModule ( "unicodedata" , typeof ( IronPython . Modules . unicodedata ) ) ]
2325namespace IronPython . Modules {
2426 public static class unicodedata {
2527 private static UCD ucd_5_2_0 = null ;
@@ -41,6 +43,14 @@ public static UCD ucd_3_2_0 {
4143
4244 [ SpecialName ]
4345 public static void PerformModuleReload ( PythonContext /*!*/ context , IDictionary /*!*/ dict ) {
46+ EnsureInitialized ( ) ;
47+ }
48+
49+ /// <summary>
50+ /// Ensures that the modules is initialized so that static methods don't throw.
51+ /// </summary>
52+ [ MemberNotNull ( nameof ( ucd_5_2_0 ) ) ]
53+ internal static void EnsureInitialized ( ) {
4454 if ( ucd_5_2_0 == null ) {
4555 // This is a lie. The version of Unicode depends on the .NET version as well as the OS. The
4656 // version of the database stored internally is 5.2, so just say that.
@@ -52,9 +62,18 @@ public static string lookup(string name) {
5262 return ucd_5_2_0 . lookup ( name ) ;
5363 }
5464
55- public static string name ( char unichr , string @default = null ) {
56- return ucd_5_2_0 . name ( unichr , @default ) ;
57- }
65+ #nullable enable
66+
67+ public static string name ( [ NotNull ] string unichr )
68+ => ucd_5_2_0 . name ( unichr ) ;
69+
70+ public static object ? name ( [ NotNull ] string unichr , object ? @default )
71+ => ucd_5_2_0 . name ( unichr , @default ) ;
72+
73+ internal static bool TryGetName ( int rune , [ NotNullWhen ( true ) ] out string ? name )
74+ => ucd_5_2_0 . TryGetName ( rune , out name ) ;
75+
76+ #nullable restore
5877
5978 public static int @decimal ( char unichr , int @default ) {
6079 return ucd_5_2_0 . @decimal ( unichr , @default ) ;
@@ -140,20 +159,34 @@ public string lookup(string name) {
140159 return char . ConvertFromUtf32 ( nameLookup [ name ] ) ;
141160 }
142161
143- public string name ( char unichr , string @default ) {
144- if ( TryGetInfo ( unichr , out CharInfo info ) ) {
145- return info . Name ;
162+ #nullable enable
163+
164+ public string name ( [ NotNull ] string unichr )
165+ => TryGetName ( GetRune ( unichr ) , out var name ) ? name : throw PythonOps . ValueError ( "no such name" ) ;
166+
167+ public object ? name ( [ NotNull ] string unichr , object ? @default )
168+ => TryGetName ( GetRune ( unichr ) , out var name ) ? name : @default ;
169+
170+ internal bool TryGetName ( int rune , [ NotNullWhen ( true ) ] out string ? name ) {
171+ if ( TryGetInfo ( rune , out CharInfo info , excludeRanges : true ) ) {
172+ name = info . Name ;
173+ return true ;
146174 }
147- return @default ;
175+ name = null ;
176+ return false ;
148177 }
149178
150- public string name ( char unichr ) {
151- if ( TryGetInfo ( unichr , out CharInfo info ) ) {
152- return info . Name ;
179+ private int GetRune ( string unichr ) {
180+ if ( unichr . Length == 1 ) {
181+ return unichr [ 0 ] ;
182+ } else if ( unichr . Length == 2 && char . IsSurrogatePair ( unichr , 0 ) ) {
183+ return char . ConvertToUtf32 ( unichr , 0 ) ;
153184 }
154- throw PythonOps . ValueError ( "no such name ") ;
185+ throw PythonOps . TypeError ( "argument 1 must be a unicode character, not str ") ;
155186 }
156187
188+ #nullable restore
189+
157190 public int @decimal ( char unichr , int @default ) {
158191 if ( TryGetInfo ( unichr , out CharInfo info ) ) {
159192 var d = info . Numeric_Value_Decimal ;
@@ -338,12 +371,14 @@ private void BuildNameLookup() {
338371 nameLookup = database . Where ( c => ! c . Value . Name . StartsWith ( "<" ) ) . ToDictionary ( c => c . Value . Name , c => c . Key , StringComparer . OrdinalIgnoreCase ) ;
339372 }
340373
341- private bool TryGetInfo ( char unichr , out CharInfo charInfo ) {
374+ internal bool TryGetInfo ( int unichr , out CharInfo charInfo , bool excludeRanges = false ) {
342375 if ( database . TryGetValue ( unichr , out charInfo ) ) return true ;
343- foreach ( var range in ranges ) {
344- if ( range . First <= unichr && unichr <= range . Last ) {
345- charInfo = range ;
346- return true ;
376+ if ( ! excludeRanges ) {
377+ foreach ( var range in ranges ) {
378+ if ( range . First <= unichr && unichr <= range . Last ) {
379+ charInfo = range ;
380+ return true ;
381+ }
347382 }
348383 }
349384 return false ;
0 commit comments