Skip to content

app

VERSION_CHECK_TTL_SECONDS = 86400 module-attribute

app = typer.Typer(rich_markup_mode='rich', invoke_without_command=True, context_settings={'help_option_names': ['-h', '--help']}) module-attribute

cli_folder_path = pathlib.Path(__file__).parent module-attribute

folder_name = file.parent.name module-attribute

full_module = f'{__package__}.{folder_name}.{py_file_name}' module-attribute

module = importlib.import_module(full_module) module-attribute

py_file_name = file.stem module-attribute

cli_command_no_args(ctx, version_requested=None)

RenderCV is a command-line tool for rendering CVs from YAML input files. For more information, see https://docs.rendercv.com.

Source code in src/rendercv/cli/app.py
@app.callback()
def cli_command_no_args(
    ctx: typer.Context,
    version_requested: Annotated[
        bool | None, typer.Option("--version", "-v", help="Show the version")
    ] = None,
):
    """RenderCV is a command-line tool for rendering CVs from YAML input files. For more
    information, see https://docs.rendercv.com.
    """
    warn_if_new_version_is_available()

    if version_requested:
        print(f"RenderCV v{__version__}")
    elif ctx.invoked_subcommand is None:
        # No command was provided, show help
        print(ctx.get_help())
        raise typer.Exit()

fetch_and_cache_latest_version()

Fetch the latest version from PyPI and write it to the cache file.

Source code in src/rendercv/cli/app.py
def fetch_and_cache_latest_version() -> None:
    """Fetch the latest version from PyPI and write it to the cache file."""
    version_string = fetch_latest_version_from_pypi()
    if version_string:
        write_version_cache(version_string)

fetch_latest_version_from_pypi()

Fetch the latest RenderCV version string from PyPI, or None on failure.

Source code in src/rendercv/cli/app.py
def fetch_latest_version_from_pypi() -> str | None:
    """Fetch the latest RenderCV version string from PyPI, or None on failure."""
    url = "https://pypi.org/pypi/rendercv/json"
    try:
        with urllib.request.urlopen(
            url, context=ssl._create_unverified_context(), timeout=5
        ) as response:
            data = response.read()
            encoding = response.info().get_content_charset("utf-8")
            json_data = json.loads(data.decode(encoding))
            return json_data["info"]["version"]
    except Exception:
        return None

get_cache_dir()

Return the platform-appropriate cache directory for RenderCV.

Source code in src/rendercv/cli/app.py
def get_cache_dir() -> pathlib.Path:
    """Return the platform-appropriate cache directory for RenderCV."""
    if sys.platform == "win32":
        base = pathlib.Path(
            os.environ.get("LOCALAPPDATA", pathlib.Path.home() / "AppData" / "Local")
        )
    elif sys.platform == "darwin":
        base = pathlib.Path.home() / "Library" / "Caches"
    else:
        base = pathlib.Path(
            os.environ.get("XDG_CACHE_HOME", pathlib.Path.home() / ".cache")
        )
    return base / "rendercv"

get_version_cache_file()

Return the path to the version check cache file.

Source code in src/rendercv/cli/app.py
def get_version_cache_file() -> pathlib.Path:
    """Return the path to the version check cache file."""
    return get_cache_dir() / "version_check.json"

read_version_cache()

Read the cached version check data, or None if unavailable/corrupt.

Source code in src/rendercv/cli/app.py
def read_version_cache() -> dict | None:
    """Read the cached version check data, or None if unavailable/corrupt."""
    try:
        data = json.loads(get_version_cache_file().read_text(encoding="utf-8"))
        if isinstance(data, dict) and "last_check" in data and "latest_version" in data:
            return data
    except (OSError, json.JSONDecodeError, KeyError):
        pass
    return None

warn_if_new_version_is_available()

Check for a newer RenderCV version using a stale-while-revalidate cache.

Why

Uses a disk cache with background refresh so the CLI never blocks on network I/O. If the cache is stale or missing, a daemon thread refreshes it for the next invocation.

Source code in src/rendercv/cli/app.py
def warn_if_new_version_is_available() -> None:
    """Check for a newer RenderCV version using a stale-while-revalidate cache.

    Why:
        Uses a disk cache with background refresh so the CLI never blocks on
        network I/O. If the cache is stale or missing, a daemon thread refreshes
        it for the next invocation.
    """
    cache = read_version_cache()

    if not cache or (time.time() - cache["last_check"]) >= VERSION_CHECK_TTL_SECONDS:
        thread = threading.Thread(target=fetch_and_cache_latest_version, daemon=True)
        thread.start()

    if cache:
        try:
            latest = packaging.version.Version(cache["latest_version"])
            current = packaging.version.Version(__version__)
            if current < latest:
                print(
                    "\n[bold yellow]A new version of RenderCV is available!"
                    f" You are using v{__version__}, and the latest version"
                    f" is v{latest}.[/bold yellow]\n"
                )
        except packaging.version.InvalidVersion:
            pass

write_version_cache(version_string)

Write the latest version string and current timestamp to the cache file.

Source code in src/rendercv/cli/app.py
def write_version_cache(version_string: str) -> None:
    """Write the latest version string and current timestamp to the cache file."""
    cache_file = get_version_cache_file()
    try:
        cache_file.parent.mkdir(parents=True, exist_ok=True)
        cache_file.write_text(
            json.dumps({"last_check": time.time(), "latest_version": version_string}),
            encoding="utf-8",
        )
    except OSError:
        pass