Resources and archives
How to include resources such as images and config files in your project.
There are two ways to include resource files in your project: resources
targets and files
targets.
resources
A resources
target is for files that are members of code packages, and are loaded via language-specific mechanisms, such as Python's pkgutil.get_data()
or Java's getResource()
.
Pants will make resources available on the appropriate runtime path, such as Python's PYTHONPATH
or the JVM classpath. Resources can be loaded directly from a binary in which they are embedded, such as a Pex file, without first unpacking it.
For example, to load resources in Python:
- src/python/project/app.py
- src/python/project/BUILD
- src/python/project/config.json
import pkgutil
if __name__ == "__main__":
config = pkgutil.get_data("project", "config.json").decode("utf-8")
print(f"Config: {config}")
python_library(
# Pants cannot infer this dependency, so we explicitly add it.
dependencies=[":config"],
)
resources(
name="config",
sources=["config.json"],
)
{ "k1": "v", "k2": "v" }
Source root stripping applies to resources, just as it does for code. In the example above, Python loads the resource named project/config
, rather than src/python/project/config.json
.
files
A files
target is for loose files that are copied into the chroot where Pants runs your code. You can then load these files through direct mechanisms like Python's open()
or Java's FileInputStream
. The files are not associated with a code package, and must be extracted out of a deployed archive file before they can be loaded.
For example, to load loose files in Python:
- src/python/project/app.py
- src/python/project/BUILD
- src/python/project/config.json
def test_open_file():
with open("src/python/project/config.json") as f:
content = f.read().decode()
assert content == '{"k1": "v", "k2": "v"}'
python_tests(
# Pants cannot infer this dependency, so we explicitly add it.
dependencies=[":config"],
)
files(
name="config",
sources=["config.json"],
)
{ "k1": "v", "k2": "v" }
Note that we open the file with its full path, including the src/python
prefix.
files
are not included with binaries like pex_binary
Pants will not include dependencies on files
targets when creating binaries like pex_binary
and python_awslambda
via ./pants package
. Filesystem APIs like Python's open()
are relative to the current working directory, and they would try to read the files from where the binary is executed, rather than reading from the binary itself.
Instead, use resources
targets or an archive
target.
When to use resources
Use resources
for files that are associated with (and typically live alongside) the code that loads them. That code's target (e.g. the python_library
) should depend on the resources
target, ensuring that code and data together are embedded directly in a binary package, such as a wheel, Pex file or AWS Lambda.
When to use files
Use files
for files that aren't tightly coupled to any specific code, but need to be deployed alongside a binary, such as images served by a web server.
When writing tests, it is also often more convenient to open a file than to load a resource.
resources | files | |
---|---|---|
Runtime path | Relative to source root | Relative to repo root |
Loading mechanism | Language's package loader, relative to package | Language's file loading idioms, relative to repo root |
Use with | Targets that produce binaries, such as pex_binary , python_distribution , python_awslambda . | archive targets, tests |
relocated_files
When you use a files
target, Pants will preserve the path to the files, relative to your build root. For example, the file src/assets/logo.png
in your repo would be under this same path in the runtime chroot.
However, you may want to change the path to something else. For example, when creating an archive
target and setting the files
field, you might want those files to be placed at a different path in the archive; rather than src/assets/logo.png
, for example, you might want the file to be at imgs/logo.png
.
You can use the relocated_files
target to change the path used at runtime for the files. Your other targets can then add this target to their dependencies
field, rather than using the original files
target:
# Original files target.
files(
name="logo",
sources=["logo.png"],
)
# At runtime, the file will be `imgs/logo.png`.
relocated_files(
name="relocated_logo",
files_targets=[":logo"],
src="src/assets",
dest="imgs",
)
As you'd expect, you can use an empty string in the src
to add to an existing prefix, and an empty string in the dest
to strip an existing prefix.
If you want multiple different re-mappings for the same original files, you can define multiple relocated_files
targets.
The relocated_files
target only accepts files
targets in its files_targets
field. To relocate where other targets like resources
and python_library
show up at runtime, you need to change where that code is located in your repository.
archive
: create a zip
or tar
file
Running ./pants package
on an archive
target will create a zip or tar file with built packages and/or loose files included. This is often useful when you want to create a binary and bundle it with some loose config files.
For example:
archive(
name="app_with_config",
packages=[":app"],
files=[":production_config"],
format="tar.xz",
)
The format can be zip
, tar
, tar.xz
, tar.gz
, or tar.bz2
.
The packages
field is a list of targets that can be built using ./pants package
, such as pex_binary
, python_awslambda
, and even other archive
targets. Pants will build the packages as if you had run ./pants package
. It will include the results in your archive using the same name they would normally have, but without the dist/
prefix.
The files
field is a list of files
and/or relocated_files
targets. See resources for more details.
You can optionally set the field output_path
to change the generated archive's name.