diff --git a/src/prx/main.py b/src/prx/main.py index 7a682e29..eae0991c 100644 --- a/src/prx/main.py +++ b/src/prx/main.py @@ -7,8 +7,8 @@ import pandas as pd import numpy as np import git -import prx.util -from prx import atmospheric_corrections as atmo, util +import prx.util as util +from prx import atmospheric_corrections as atmo from prx.constants import carrier_frequencies_hz from prx.rinex_obs.parser import parse_rinex_obs_file from prx.util import is_rinex_3_obs_file, is_rinex_3_nav_file @@ -20,7 +20,7 @@ log = util.get_logger(__name__) -@prx.util.timeit +@util.timeit def write_prx_file( prx_header: dict, prx_records: pd.DataFrame, @@ -144,9 +144,14 @@ def build_metadata(input_files): } for file in files ] - prx_metadata["prx_git_commit_id"] = git.Repo( - path=Path(__file__).parent, search_parent_directories=True - ).head.object.hexsha + try: + prx_metadata["prx_git_commit_id"] = ( + util.git_sha_of_this_package() + or util.git_sha_from_dist_info("prx") + or "unknown" + ) + except git.exc.InvalidGitRepositoryError: + prx_metadata["prx_git_commit_id"] = "not_a_git_repository" return prx_metadata @@ -177,7 +182,7 @@ def warm_up_parser_cache(rinex_files): _ = [parse_rinex_nav_or_obs_file(file) for file in rinex_files] -@prx.util.timeit +@util.timeit def build_records_levels_12( rinex_3_obs_file, rinex_3_ephemerides_files, @@ -378,7 +383,7 @@ def assign_carrier_frequencies(flat_obs): return flat_obs -@prx.util.timeit +@util.timeit def process(observation_file_path: Path, prx_level=2, model_tropo="saastamoinen"): t0 = pd.Timestamp.now() # We expect a Path, but might get a string here: @@ -387,7 +392,7 @@ def process(observation_file_path: Path, prx_level=2, model_tropo="saastamoinen" f"Starting processing {observation_file_path.name} (full path {observation_file_path})" ) rinex_3_obs_file = converters.anything_to_rinex_3(observation_file_path) - rinex_3_obs_file = prx.util.try_repair_with_gfzrnx(rinex_3_obs_file) + rinex_3_obs_file = util.try_repair_with_gfzrnx(rinex_3_obs_file) prx_file = rinex_3_obs_file.with_suffix("") match prx_level: case 1 | 2: diff --git a/src/prx/util.py b/src/prx/util.py index a4a4fe75..37be1d60 100644 --- a/src/prx/util.py +++ b/src/prx/util.py @@ -1,3 +1,4 @@ +import json import logging import math import os @@ -6,6 +7,8 @@ import subprocess from functools import wraps from pathlib import Path +import importlib.metadata as md +import git import georinex import joblib @@ -482,3 +485,39 @@ def compute_gps_utc_leap_seconds(yyyy: int, doy: int): break assert ~np.isnan(ls), "GPS leap second could not be retrieved" return ls + + +def git_sha_of_this_package() -> str | None: + if (Path(__file__).parent.resolve() / ".git").exists(): + repo = git.Repo(path=Path(__file__).parent) + git_commit_id = ( + f"{repo.head.object.hexsha}{'_dirty' if repo.is_dirty() else ''}" + ) + return git_commit_id + return None # not a git checkout (e.g., installed wheel) + + +def git_sha_from_dist_info(dist_name: str) -> str | None: + try: + dist = md.distribution(dist_name) + except md.PackageNotFoundError: + return None + + # Look for the PEP 610 file + direct_url = Path(dist.locate_file("direct_url.json")) + if not direct_url.exists(): + # some installers nest it inside the dist-info directory + dist_info_dir = Path(dist._path) if hasattr(dist, "_path") else None + if dist_info_dir: + direct_url = dist_info_dir / "direct_url.json" + if not direct_url.exists(): + return None + + try: + data = json.loads(direct_url.read_text(encoding="utf-8")) + # Expected shape for VCS installs: + # {"url": "...", "vcs_info": {"vcs": "git", "requested_revision": "...", "commit_id": "..."}} + vcs_info = data.get("vcs_info") or {} + return vcs_info.get("commit_id") or vcs_info.get("requested_revision") + except Exception: + return None