]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-12915: Improve Unicode support for package names and attributes. (GH-18517)
authorVinay Sajip <vinay_sajip@yahoo.co.uk>
Fri, 28 Feb 2020 14:26:27 +0000 (14:26 +0000)
committerGitHub <noreply@github.com>
Fri, 28 Feb 2020 14:26:27 +0000 (14:26 +0000)
Lib/pkgutil.py
Lib/test/test_pkgutil.py

index 4bc3083ac197eb86a1a9e33e05ebf27ba4860fa4..4c184678a29128f03c9edbcdf0dd47f35dcbe9bd 100644 (file)
@@ -638,8 +638,8 @@ def get_data(package, resource):
     return loader.get_data(resource_name)
 
 
-_DOTTED_WORDS = r'[a-z_]\w*(\.[a-z_]\w*)*'
-_NAME_PATTERN = re.compile(f'^({_DOTTED_WORDS})(:({_DOTTED_WORDS})?)?$', re.I)
+_DOTTED_WORDS = r'(?!\d)(\w+)(\.(?!\d)(\w+))*'
+_NAME_PATTERN = re.compile(f'^(?P<pkg>{_DOTTED_WORDS})(?P<cln>:(?P<obj>{_DOTTED_WORDS})?)?$', re.U)
 del _DOTTED_WORDS
 
 def resolve_name(name):
@@ -677,11 +677,12 @@ def resolve_name(name):
     m = _NAME_PATTERN.match(name)
     if not m:
         raise ValueError(f'invalid format: {name!r}')
-    groups = m.groups()
-    if groups[2]:
+    gd = m.groupdict()
+    if gd.get('cln'):
         # there is a colon - a one-step import is all that's needed
-        mod = importlib.import_module(groups[0])
-        parts = groups[3].split('.') if groups[3] else []
+        mod = importlib.import_module(gd['pkg'])
+        parts = gd.get('obj')
+        parts = parts.split('.') if parts else []
     else:
         # no colon - have to iterate to find the package boundary
         parts = name.split('.')
index 906150b10495bfbdc43749cfa68c6fb846a0565f..53456c2f7659e2e05c2bd393e37cb8ec3b3ccfcc 100644 (file)
@@ -229,8 +229,40 @@ class PkgutilTests(unittest.TestCase):
             ('logging.handlers:SysLogHandler.NO_SUCH_VALUE', AttributeError),
             ('logging.handlers.SysLogHandler.NO_SUCH_VALUE', AttributeError),
             ('ZeroDivisionError', ImportError),
+            ('os.path.9abc', ValueError),
+            ('9abc', ValueError),
         )
 
+        # add some Unicode package names to the mix.
+
+        unicode_words = ('\u0935\u092e\u0938',
+                         '\xe9', '\xc8',
+                         '\uc548\ub155\ud558\uc138\uc694',
+                         '\u3055\u3088\u306a\u3089',
+                         '\u3042\u308a\u304c\u3068\u3046',
+                         '\u0425\u043e\u0440\u043e\u0448\u043e',
+                         '\u0441\u043f\u0430\u0441\u0438\u0431\u043e',
+                         '\u73b0\u4ee3\u6c49\u8bed\u5e38\u7528\u5b57\u8868')
+
+        for uw in unicode_words:
+            d = os.path.join(self.dirname, uw)
+            os.makedirs(d, exist_ok=True)
+            # make an empty __init__.py file
+            f = os.path.join(d, '__init__.py')
+            with open(f, 'w') as f:
+                f.write('')
+                f.flush()
+            # now import the package we just created; clearing the caches is
+            # needed, otherwise the newly created package isn't found
+            importlib.invalidate_caches()
+            mod = importlib.import_module(uw)
+            success_cases += (uw, mod),
+            if len(uw) > 1:
+                failure_cases += (uw[:-1], ImportError),
+
+        # add an example with a Unicode digit at the start
+        failure_cases += ('\u0966\u0935\u092e\u0938', ValueError),
+
         for s, expected in success_cases:
             with self.subTest(s=s):
                 o = pkgutil.resolve_name(s)