summaryrefslogtreecommitdiffstats
path: root/third_party/python/pkgutil_resolve_name/pkgutil_resolve_name.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/python/pkgutil_resolve_name/pkgutil_resolve_name.py
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/python/pkgutil_resolve_name/pkgutil_resolve_name.py')
-rw-r--r--third_party/python/pkgutil_resolve_name/pkgutil_resolve_name.py112
1 files changed, 112 insertions, 0 deletions
diff --git a/third_party/python/pkgutil_resolve_name/pkgutil_resolve_name.py b/third_party/python/pkgutil_resolve_name/pkgutil_resolve_name.py
new file mode 100644
index 0000000000..10bb99c362
--- /dev/null
+++ b/third_party/python/pkgutil_resolve_name/pkgutil_resolve_name.py
@@ -0,0 +1,112 @@
+"""
+Resolve a name to an object.
+
+It is expected that `name` will be a string in one of the following
+formats, where W is shorthand for a valid Python identifier and dot stands
+for a literal period in these pseudo-regexes:
+
+W(.W)*
+W(.W)*:(W(.W)*)?
+
+The first form is intended for backward compatibility only. It assumes that
+some part of the dotted name is a package, and the rest is an object
+somewhere within that package, possibly nested inside other objects.
+Because the place where the package stops and the object hierarchy starts
+can't be inferred by inspection, repeated attempts to import must be done
+with this form.
+
+In the second form, the caller makes the division point clear through the
+provision of a single colon: the dotted name to the left of the colon is a
+package to be imported, and the dotted name to the right is the object
+hierarchy within that package. Only one import is needed in this form. If
+it ends with the colon, then a module object is returned.
+
+The function will return an object (which might be a module), or raise one
+of the following exceptions:
+
+ValueError - if `name` isn't in a recognised format
+ImportError - if an import failed when it shouldn't have
+AttributeError - if a failure occurred when traversing the object hierarchy
+ within the imported package to get to the desired object)
+"""
+
+import importlib
+import re
+
+__version__ = "1.3.10"
+
+
+_NAME_PATTERN = None
+
+def resolve_name(name):
+ """
+ Resolve a name to an object.
+
+ It is expected that `name` will be a string in one of the following
+ formats, where W is shorthand for a valid Python identifier and dot stands
+ for a literal period in these pseudo-regexes:
+
+ W(.W)*
+ W(.W)*:(W(.W)*)?
+
+ The first form is intended for backward compatibility only. It assumes that
+ some part of the dotted name is a package, and the rest is an object
+ somewhere within that package, possibly nested inside other objects.
+ Because the place where the package stops and the object hierarchy starts
+ can't be inferred by inspection, repeated attempts to import must be done
+ with this form.
+
+ In the second form, the caller makes the division point clear through the
+ provision of a single colon: the dotted name to the left of the colon is a
+ package to be imported, and the dotted name to the right is the object
+ hierarchy within that package. Only one import is needed in this form. If
+ it ends with the colon, then a module object is returned.
+
+ The function will return an object (which might be a module), or raise one
+ of the following exceptions:
+
+ ValueError - if `name` isn't in a recognised format
+ ImportError - if an import failed when it shouldn't have
+ AttributeError - if a failure occurred when traversing the object hierarchy
+ within the imported package to get to the desired object)
+ """
+ global _NAME_PATTERN
+ if _NAME_PATTERN is None:
+ # Lazy import to speedup Python startup time
+ import re
+ dotted_words = r'(?!\d)(\w+)(\.(?!\d)(\w+))*'
+ _NAME_PATTERN = re.compile(f'^(?P<pkg>{dotted_words})'
+ f'(?P<cln>:(?P<obj>{dotted_words})?)?$',
+ re.UNICODE)
+
+ m = _NAME_PATTERN.match(name)
+ if not m:
+ raise ValueError(f'invalid format: {name!r}')
+ gd = m.groupdict()
+ if gd.get('cln'):
+ # there is a colon - a one-step import is all that's needed
+ 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('.')
+ modname = parts.pop(0)
+ # first part *must* be a module/package.
+ mod = importlib.import_module(modname)
+ while parts:
+ p = parts[0]
+ s = f'{modname}.{p}'
+ try:
+ mod = importlib.import_module(s)
+ parts.pop(0)
+ modname = s
+ except ImportError:
+ break
+ # if we reach this point, mod is the module, already imported, and
+ # parts is the list of parts in the object hierarchy to be traversed, or
+ # an empty list if just the module is wanted.
+ result = mod
+ for p in parts:
+ result = getattr(result, p)
+ return result