Files
Jyotisha/scripts/jyotishyamitra_adapter_probe.py
T
2026-07-18 01:00:53 +08:00

362 lines
13 KiB
Python

#!/usr/bin/env python3
"""Probe jyotishyamitra as an independent oracle without copying implementation."""
from __future__ import annotations
import argparse
import copy
import hashlib
import json
import os
import subprocess
import sys
import zipfile
from pathlib import Path
from tempfile import TemporaryDirectory
from scripts.xalen_oracle_comparison import RASHI
VERSION = "1.4.0"
COMMIT = "86f7eb610a66b06b3f0817d2c53355bec8b3bf8d"
LICENSE = "MIT"
RETURNVAL = "ASTRODATA_DICTIONARY"
SIGN_ALIASES = {"Saggitarius": "Sagittarius"}
def _sha256_bytes(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def _stable_json(data: object) -> str:
return json.dumps(data, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def wheel_metadata(wheel_path: Path) -> dict:
with zipfile.ZipFile(wheel_path) as zf:
metadata_name = next(name for name in zf.namelist() if name.endswith(".dist-info/METADATA"))
wheel_name = next(name for name in zf.namelist() if name.endswith(".dist-info/WHEEL"))
license_name = next((name for name in zf.namelist() if name.endswith(".dist-info/licenses/LICENSE")), None)
metadata_text = zf.read(metadata_name).decode("utf-8", "replace")
wheel_text = zf.read(wheel_name).decode("utf-8", "replace")
license_text = zf.read(license_name).decode("utf-8", "replace") if license_name else ""
fields = {}
for line in metadata_text.splitlines():
if ": " in line:
key, value = line.split(": ", 1)
fields.setdefault(key, value)
tags = [line.split(": ", 1)[1] for line in wheel_text.splitlines() if line.startswith("Tag: ")]
return {
"metadata_name": metadata_name,
"wheel_name": wheel_name,
"name": fields.get("Name"),
"version": fields.get("Version"),
"license": fields.get("License"),
"license_file_name": license_name,
"license_file_sha256": _sha256_bytes(license_text.encode("utf-8")) if license_text else None,
"license_file_spdx_inferred": "MIT" if "MIT License" in license_text else None,
"requires_python": fields.get("Requires-Python"),
"summary": fields.get("Summary"),
"wheel_tags": tags,
"metadata_sha256": _sha256_bytes(metadata_text.encode("utf-8")),
"wheel_record_sha256": _sha256_bytes(wheel_text.encode("utf-8")),
}
def canonical_request(case: dict) -> dict:
birth = case["birth"]
return {
"name": case.get("name", "public_case"),
"gender": case.get("gender", "male"),
"place": case.get("place", "unknown"),
"longitude": case["longitude"],
"latitude": case["latitude"],
"timezone": case["timezone"],
"birth": {
"year": birth["year"],
"month": birth["month"],
"day": birth["day"],
"hour": birth["hour"],
"minute": birth["minute"],
"second": birth.get("second", 0),
},
"ayanamsa": case.get("ayanamsa", "package_default"),
"node_mode": case.get("node_mode", "package_default"),
"returnval": RETURNVAL,
}
def schema_fingerprint(raw: object) -> dict:
paths = []
def walk(value: object, prefix: str = "$") -> None:
if isinstance(value, dict):
for key in sorted(value):
walk(value[key], f"{prefix}.{key}")
elif isinstance(value, list):
paths.append(f"{prefix}[]")
if value:
walk(value[0], f"{prefix}[]")
else:
paths.append(f"{prefix}:{type(value).__name__}")
walk(raw)
joined = "\n".join(paths)
return {"path_count": len(paths), "sha256": _sha256_bytes(joined.encode("utf-8")), "sample_paths": paths[:80]}
def normalize_raw(raw: object) -> object:
data = copy.deepcopy(raw)
try:
data["Dashas"]["Vimshottari"]["current"]["date"] = "<volatile_run_time>"
except (TypeError, KeyError):
pass
return data
def run_installed_jyotishyamitra(case: dict) -> object:
import importlib
jm = importlib.import_module("jyotishyamitra")
request = canonical_request(case)
birth = request["birth"]
data = jm.input_birthdata(
name=request["name"],
gender=request["gender"],
place=request["place"],
longitude=str(request["longitude"]),
lattitude=str(request["latitude"]),
timezone=str(request["timezone"]),
year=str(birth["year"]),
month=str(birth["month"]),
day=str(birth["day"]),
hour=str(birth["hour"]),
min=str(birth["minute"]),
sec=str(birth.get("second", 0)),
)
validation = jm.validate_birthdata()
if validation != "SUCCESS":
return {"status": "INPUT_ERROR", "validation": validation, "input": data}
return jm.generate_astrologicalData(jm.get_birthdata(), returnval=request["returnval"])
def run_isolated(wheel_path: Path, case: dict) -> dict:
with TemporaryDirectory() as tmp:
subprocess.run(
[sys.executable, "-m", "pip", "install", "--no-deps", "--target", tmp, str(wheel_path)],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
code = (
"import json,sys;"
"sys.path.insert(0, sys.argv[1]);"
"from scripts.jyotishyamitra_adapter_probe import run_installed_jyotishyamitra;"
"case=json.loads(sys.stdin.read());"
"print(json.dumps(run_installed_jyotishyamitra(case), ensure_ascii=False, sort_keys=True))"
)
env = {**os.environ, "PYTHONPATH": str(Path.cwd())}
done = subprocess.run(
[sys.executable, "-c", code, tmp],
input=_stable_json(case),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
env=env,
check=False,
)
raw = json.loads(done.stdout) if done.returncode == 0 and done.stdout.strip().startswith(("{", "[", '"')) else done.stdout.strip()
return {
"install_path": tmp,
"returncode": done.returncode,
"stderr": done.stderr.strip(),
"raw": raw,
}
def extract_fields(raw: dict) -> dict:
keys = ("D1", "D2", "D4", "D9", "D10", "ashtakavarga", "shadbala", "vimshottari")
lowered = {str(k).lower(): k for k in raw}
out = {}
for key in keys:
source = lowered.get(key.lower())
if source is not None:
out[key] = raw[source]
return out
def extract_varga_signs(raw: dict) -> dict:
out = {}
for chart in ("D1", "D2", "D4", "D9", "D10"):
planets = (raw.get(chart) or {}).get("planets") or {}
signs = {
planet: SIGN_ALIASES.get(str(data["sign"]), str(data["sign"]))
for planet, data in planets.items()
if isinstance(data, dict) and isinstance(data.get("sign"), str) and data["sign"]
}
if signs:
out[chart] = signs
return out
def extract_local_varga_signs(raw: dict) -> dict:
d1 = raw.get("planets") or {}
varga = raw.get("varga") or {}
surfaces = {"D1": d1, **varga}
return {
chart: {
planet: data["sign"]
for planet, data in planets.items()
if isinstance(data, dict) and isinstance(data.get("sign"), str) and data["sign"]
}
for chart, planets in surfaces.items()
if chart in {"D1", "D2", "D4", "D9", "D10"} and isinstance(planets, dict)
}
def extract_xalen_varga_signs(raw: dict) -> dict:
varga = raw.get("varga") or {}
return {
chart: {
planet: RASHI.get(str(sign), str(sign))
for planet, sign in planets.items()
if isinstance(sign, str) and sign
}
for chart, planets in varga.items()
if chart in {"D1", "D2", "D4", "D9", "D10"} and isinstance(planets, dict)
}
def build_varga_comparison(jyotishyamitra_raw: dict, local_raw: dict, xalen_raw: dict) -> dict:
return compare_with_existing_oracles(
extract_varga_signs(jyotishyamitra_raw),
extract_local_varga_signs(local_raw),
extract_xalen_varga_signs(xalen_raw.get("raw", xalen_raw)),
)
def compare_with_existing_oracles(jyotishyamitra: dict, local: dict, xalen: dict) -> dict:
rows = []
counts = {"local": 0, "xalen": 0}
for section, fields in jyotishyamitra.items():
if not isinstance(fields, dict):
continue
for field, value in fields.items():
local_value = (local.get(section) or {}).get(field)
xalen_value = (xalen.get(section) or {}).get(field)
local_match = local_value is not None and value == local_value
xalen_match = xalen_value is not None and value == xalen_value
counts["local"] += int(local_match)
counts["xalen"] += int(xalen_match)
rows.append(
{
"section": section,
"field": field,
"jyotishyamitra_value": value,
"local_value": local_value,
"xalen_value": xalen_value,
"local_status": "not_comparable" if local_value is None else ("match" if local_match else "mismatch"),
"xalen_status": "not_comparable" if xalen_value is None else ("match" if xalen_match else "mismatch"),
}
)
return {
"scope": "jyotishyamitra_field_comparison",
"row_count": len(rows),
"match_counts": counts,
"promotion_allowed": False,
"truth_policy": "independent_observation_not_truth",
"rows": rows,
}
def build_report(
wheel_path: Path,
commit: str = COMMIT,
raw: dict | None = None,
comparison: dict | None = None,
request: dict | None = None,
isolated_run: dict | None = None,
) -> dict:
if not wheel_path.exists():
return {
"scope": "jyotishyamitra_pinned_adapter_probe",
"oracle": "jyotishyamitra",
"version": VERSION,
"source_commit": commit,
"license": LICENSE,
"wheel_path": str(wheel_path),
"status": "blocked",
"blocked_reason": "fixture_missing",
"truth_policy": "independent_observation_not_truth",
"promotion_allowed": False,
"boundary": "Wheel fixture is required; adapter must not download at test/runtime or use fake zip evidence.",
}
raw = raw or {}
normalized = normalize_raw(raw)
wheel_hash = _sha256_bytes(wheel_path.read_bytes()) if wheel_path.exists() else None
meta = wheel_metadata(wheel_path) if wheel_path.exists() else {}
return {
"scope": "jyotishyamitra_pinned_adapter_probe",
"oracle": "jyotishyamitra",
"version": VERSION,
"source_commit": commit,
"source_url": f"https://github.com/VicharaVandana/jyotishyamitra/commit/{commit}",
"package_url": "https://pypi.org/project/jyotishyamitra/1.4.0/",
"license": LICENSE,
"package_metadata": meta,
"python_runtime": {
"executable": sys.executable,
"version": sys.version.split()[0],
"wheel_tags": meta.get("wheel_tags", []),
},
"canonical_request": request or {},
"isolated_subprocess": {
"used": isolated_run is not None,
"returncode": None if isolated_run is None else isolated_run["returncode"],
"temporary_install_path": None if isolated_run is None else isolated_run["install_path"],
"stderr": None if isolated_run is None else isolated_run["stderr"],
},
"wheel_path": str(wheel_path),
"wheel_sha256": wheel_hash,
"raw_sha256": _sha256_bytes(_stable_json(raw).encode("utf-8")),
"normalized_raw_sha256": _sha256_bytes(_stable_json(normalized).encode("utf-8")),
"normalization": {
"volatile_paths": ["$.Dashas.Vimshottari.current.date"],
"purpose": "remove run timestamp before replay comparison",
},
"schema_fingerprint": schema_fingerprint(raw),
"raw": raw,
"comparison": comparison or {},
"truth_policy": "independent_observation_not_truth",
"promotion_allowed": False,
"status": "stable_raw_ready_as_independent_observation" if raw and not (isinstance(raw, dict) and raw.get("status") == "INPUT_ERROR") else "metadata_only_or_input_blocked",
"boundary": "Adapter calls the installed MIT package as an oracle; it does not copy implementation or promote conclusions.",
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--wheel", type=Path, default=Path("/tmp/jyotishyamitra_probe/jyotishyamitra-1.4.0-py3-none-any.whl"))
parser.add_argument("--case-json", type=Path)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
raw = {}
request = {}
isolated = None
if args.case_json:
case = json.loads(args.case_json.read_text(encoding="utf-8"))
request = canonical_request(case)
isolated = run_isolated(args.wheel, case)
raw = isolated["raw"]
report = build_report(args.wheel, raw=raw, request=request, isolated_run=isolated)
text = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + "\n"
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(text, encoding="utf-8")
sys.stdout.write(text)
return 0
if __name__ == "__main__":
raise SystemExit(main())