blob: 82442996500608cee99ab62d1b511d96888035e4 [file] [edit]
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# Wrapper script for mypy_primer that generates a Pyrefly config for the real
# pyrefly binary before forwarding arguments.
#
# mypy_primer calls: {pyrefly} check {paths} --summary=none --output-format min-text
# This wrapper intercepts that call, generates a config for the current
# directory, then forwards the original arguments to the real pyrefly binary.
#
# When PYREFLY_SCRIPTS_DIR is set, the wrapper also sets up project dependencies
# via setup_primer_deps.py, giving pyrefly access to third-party type stubs
# and reducing Unknown-type noise in primer results.
# The real pyrefly binary sits alongside this script with a "-real" suffix.
REAL_PYREFLY="$(dirname "$0")/pyrefly-real"
# mypy_primer runs old and new pyrefly concurrently (asyncio.gather) in the
# same project directory. Without serialization, the two wrapper instances race
# on venv creation. We use mkdir as a portable atomic lock to ensure only one
# instance performs shared dependency setup; the second waits, then reuses it.
#
# Config migration is intentionally not shared: a new pyrefly binary can emit
# config error codes that the old binary cannot parse. Each real binary writes a
# private generated config and checks with --config so primer compares checker
# behavior rather than config-parser compatibility.
# TODO/BE: cleaner solution is to run the setup/initialization steps once before running both old/new pyrefly binaries
LOCKDIR=".pyrefly_primer.lock"
if mkdir "$LOCKDIR" 2>/dev/null; then
# We won the lock — perform shared dependency setup.
if [ -n "$PYREFLY_SCRIPTS_DIR" ]; then
PROJECT_NAME=$(basename "$(pwd)")
if [ ! -d ".venv" ]; then
PYTHONPATH="$PYREFLY_SCRIPTS_DIR" python3 \
"$PYREFLY_SCRIPTS_DIR/setup_primer_deps.py" "$PROJECT_NAME" >/dev/null 2>&1 || true
fi
fi
# Signal that setup is complete.
touch "$LOCKDIR/done"
else
# Another instance is running setup — wait for it to finish.
while [ ! -f "$LOCKDIR/done" ]; do
sleep 0.2
done
fi
CONFIG_TAG=$(printf "%s" "$REAL_PYREFLY" | cksum | cut -d " " -f 1)
CONFIG_PATH=".pyrefly_primer_${CONFIG_TAG}.toml"
CONFIG_TMP="${CONFIG_PATH}.tmp"
if [ ! -f "$CONFIG_PATH" ]; then
if "$REAL_PYREFLY" init --non-interactive --dry-run --print-config . >"$CONFIG_TMP" 2>/dev/null; then
mv "$CONFIG_TMP" "$CONFIG_PATH"
else
rm -f "$CONFIG_TMP"
fi
fi
# Extract site-package paths (read-only, safe to run after setup is complete).
SITE_PATH_ARGS=()
if [ -n "$PYREFLY_SCRIPTS_DIR" ] && [ -f ".venv/bin/python" ]; then
readarray -t SITE_PATH_ARGS < <(
.venv/bin/python -c "
import site
for p in site.getsitepackages():
print('--site-package-path=' + p)
"
)
fi
# Forward all original arguments to the real pyrefly binary.
# For `check`, enable every error kind (--preset=all) so primer surfaces
# off-by-default error deltas, matching the internal continuous mypy_primer job.
# This applies to both the old and new binaries, since both run through this
# wrapper, keeping the diff a like-for-like comparison.
CHECK_ARGS=()
if [ "$1" = "check" ]; then
CHECK_ARGS+=(--preset=all)
fi
# Normalize the per-binary config filename in output so that old and new
# binaries produce identical warnings and primer doesn't report spurious diffs.
if [ "$1" = "check" ] && [ -f "$CONFIG_PATH" ]; then
"$REAL_PYREFLY" "$@" "${SITE_PATH_ARGS[@]}" "${CHECK_ARGS[@]}" --config "$CONFIG_PATH" 2>&1 \
| sed "s|$CONFIG_PATH|pyrefly.toml|g"
exit "${PIPESTATUS[0]}"
fi
exec "$REAL_PYREFLY" "$@" "${SITE_PATH_ARGS[@]}" "${CHECK_ARGS[@]}"