Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ sphinx-build docs build/html
Zipapps created with shiv are not guaranteed to be cross-compatible with other architectures. For example, a `pyz`
file built on a Mac may only work on other Macs, likewise for RHEL, etc. This usually only applies to zipapps that have C extensions in their dependencies. If all your dependencies are pure python, then chances are the `pyz` _will_ work on other platforms. Just something to be aware of.

Zipapps created with shiv *will* extract themselves into `~/.shiv`, unless overridden via
Zipapps created with shiv *will* extract themselves into `~/.cache/shiv` (platform dependent), unless overridden via
`SHIV_ROOT`. If you create many utilities with shiv, you may want to occasionally clean this
directory.

Expand Down
6 changes: 3 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Bootstrapping
^^^^^^^^^^^^^

As mentioned above, when you run an executable created with ``shiv``, a special bootstrap function is called.
This function unpacks the dependencies into a uniquely named subdirectory of ``~/.shiv`` and then runs your entry point
This function unpacks the dependencies into a uniquely named subdirectory of ``~/.cache/shiv`` (platform dependent) and then runs your entry point
(or interactive interpreter) with those dependencies added to your interpreter's search path (``sys.path``).

To improve performance, once the dependencies have been extracted to disk, any further invocations will re-use the 'cached'
Expand Down Expand Up @@ -126,7 +126,7 @@ If the preamble file is written in Python (e.g. ends in ``.py``) then shiv will
* ``env``: an instance of the `Environment <api:bootstrap.environment.Environment>`_ object.
* ``site_packages``: a :py:class:`pathlib.Path` of the directory where the current PYZ's site_packages were extracted to during bootstrap.

For an example, a preamble file that cleans up prior extracted ``~/.shiv`` directories might look like:
For an example, a preamble file that cleans up prior extracted ``~/.cache/shiv`` directories might look like:

.. code-block:: py

Expand Down Expand Up @@ -225,7 +225,7 @@ you can specify to influence a zipapp created with shiv at run time.
SHIV_ROOT
^^^^^^^^^

This should be populated with a full path, it overrides ``~/.shiv`` as the default base dir for shiv's extraction cache.
This should be populated with a full path, it overrides ``~/.cache/shiv`` (platform dependent) as the default base dir for shiv's extraction cache.

This is useful if you want to collect the contents of a zipapp to inspect them, or if you want to make a quick edit to
a source file, but don't want to taint the extraction cache.
Expand Down
31 changes: 28 additions & 3 deletions src/shiv/bootstrap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,33 @@ def import_string(import_name):
raise ImportError(e)


def get_default_cache_root():
"""Returns the platform dependent default cache directory."""

# Previous versions used "~/.shiv". If it exists, continue using it.
legacy_root = Path("~/.shiv").expanduser()
if legacy_root.is_dir():
return legacy_root

# Loosely based on https://github.com/platformdirs/platformdirs

if sys.platform == "win32":
local_app_data = os.environ.get("LOCALAPPDATA", "")
if local_app_data.strip():
return Path(os.path.normpath(local_app_data)) / "shiv" / "cache"

if sys.platform == "darwin":
return Path("~/Library/Caches/shiv").expanduser()

xdg_cache_home = os.environ.get("XDG_CACHE_HOME", "")
if xdg_cache_home.strip():
return Path(os.path.normpath(xdg_cache_home)) / "shiv"

return Path("~/.cache/shiv").expanduser()


def cache_path(archive, root_dir, build_id):
"""Returns a ~/.shiv cache directory for unzipping site-packages during bootstrap.
"""Returns a cache directory for unzipping site-packages during bootstrap.

:param ZipFile archive: The zipfile object we are bootstrapping from.
:param str root_dir: Optional, either a path or environment variable pointing to a SHIV_ROOT.
Expand All @@ -102,7 +127,7 @@ def cache_path(archive, root_dir, build_id):

root_dir = Path(root_dir).expanduser()

root = root_dir or Path("~/.shiv").expanduser()
root = root_dir or get_default_cache_root()
name = Path(archive.filename).resolve().name
return root / f"{name}_{build_id}"

Expand All @@ -120,7 +145,7 @@ def extract_site_packages(archive, target_path, compile_pyc=False, compile_worke
target_path_tmp = Path(parent, target_path.name + ".tmp")
lock = Path(parent, f".{target_path.name}_lock")

# If this is the first time that a pyz is being extracted, we'll need to create the ~/.shiv dir
# If this is the first time that a pyz is being extracted, we'll need to create the cache root dir
if not parent.exists():
parent.mkdir(parents=True, exist_ok=True)

Expand Down
8 changes: 6 additions & 2 deletions src/shiv/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def copytree(src: Path, dst: Path) -> None:
is_flag=True,
help=(
"If specified, this modifies the runtime of the zipapp to raise "
"a RuntimeException if the source files (in ~/.shiv or SHIV_ROOT) have been modified. "
"a RuntimeException if the source files (in ~/.cache/shiv or SHIV_ROOT) have been modified. "
"""It's recommended to use Python's "--check-hash-based-pycs always" option with this feature."""
),
)
Expand All @@ -155,7 +155,11 @@ def copytree(src: Path, dst: Path) -> None:
"but before invoking your entry point."
),
)
@click.option("--root", type=click.Path(), help="Override the 'root' path (default is ~/.shiv).")
@click.option(
"--root",
type=click.Path(),
help="Override the 'root' path (default is platform dependent, e.g. ~/.cache/shiv).",
)
@click.argument("pip_args", nargs=-1, type=click.UNPROCESSED)
def main(
output_file: str,
Expand Down