:meth:`get_data <importlib.abc.ResourceLoader.get_data>` API. The
*package* argument should be the name of a package, in standard module format
(``foo.bar``). The *resource* argument should be in the form of a relative
- filename, using ``/`` as the path separator. The parent directory name
- ``..`` is not allowed, and nor is a rooted name (starting with a ``/``).
+ filename, using ``/`` as the path separator.
The function returns a binary string that is the contents of the specified
resource.
+ This function uses the :term:`loader` method
+ :func:`~importlib.abc.FileLoader.get_data`
+ to support modules installed in the filesystem, but also in zip files,
+ databases, or elsewhere.
+
For packages located in the filesystem, which have already been imported,
this is the rough equivalent of::
d = os.path.dirname(sys.modules[package].__file__)
data = open(os.path.join(d, resource), 'rb').read()
+ Like the :func:`open` function, :func:`!get_data` can follow parent
+ directories (``../``) and absolute paths (starting with ``/`` or ``C:/``,
+ for example).
+ It can open compilation/installation artifacts like ``.py`` and ``.pyc``
+ files or files with :func:`reserved filenames <os.path.isreserved>`.
+ To be compatible with non-filesystem loaders, avoid using these features.
+
+ .. warning::
+
+ This function is intended for trusted input.
+ It does not verify that *resource* "belongs" to *package*.
+
+ If you use a user-provided *resource* path, consider verifying it.
+ For example, require an alphanumeric filename with a known extension, or
+ install and check a list of known resources.
+
If the package cannot be located or loaded, or it uses a :term:`loader`
which does not support :meth:`get_data <importlib.abc.ResourceLoader.get_data>`,
then ``None`` is returned. In particular, the :term:`loader` for
:term:`namespace packages <namespace package>` does not support
:meth:`get_data <importlib.abc.ResourceLoader.get_data>`.
+ .. seealso::
+
+ The :mod:`importlib.resources` module provides structured access to
+ module resources.
.. function:: resolve_name(name)
# signature - an os.path format "filename" starting with the dirname of
# the package's __file__
parts = resource.split('/')
- if os.path.isabs(resource) or '..' in parts:
- raise ValueError("resource must be a relative path with no "
- "parent directory components")
parts.insert(0, os.path.dirname(mod.__file__))
resource_name = os.path.join(*parts)
return loader.get_data(resource_name)
del sys.modules[pkg]
- def test_getdata_path_traversal(self):
- pkg = 'test_getdata_traversal'
-
- # Make a package with some resources
- package_dir = os.path.join(self.dirname, pkg)
- os.mkdir(package_dir)
- # Empty init.py
- f = open(os.path.join(package_dir, '__init__.py'), "wb")
- f.close()
-
- with self.assertRaises(ValueError):
- pkgutil.get_data(pkg, '../../../etc/passwd')
- with self.assertRaises(ValueError):
- pkgutil.get_data(pkg, 'sub/../../../etc/passwd')
- with self.assertRaises(ValueError):
- pkgutil.get_data(pkg, os.path.abspath('/etc/passwd'))
-
- del sys.modules[pkg]
-
def test_getdata_zipfile(self):
zip = 'test_getdata_zipfile.zip'
pkg = 'test_getdata_zipfile'