Pin utf-8 on shipping-code text I/O instead of the operator locale (#7486)

* Pin utf-8 on shipping-code text I/O instead of the operator locale

113 read_text/write_text/open call sites across unsloth, studio and
unsloth_cli let locale.getencoding() decide the encoding. That is utf-8 on
the Linux and macOS runners and cp1252 on a stock Windows install, so the
same file decodes differently for a Windows user and silently produces
mojibake or raises UnicodeDecodeError.

Adds tests/test_runtime_text_encoding.py to keep it that way. It resolves
openers through each file's own imports rather than a fixed list of module
names, so an aliased tarfile.open or a local from PIL.Image import open is
not asked for an encoding it does not take.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Scan tracked files only and resolve the unbound Path calling forms

* Honour PEP 263 when scanning sources and migrate a legacy JSONL before appending

* Scope guard imports lexically and only migrate a legacy file when it round-trips

* Leave a legacy JSONL untouched and resolve path aliases in the foreign-opener check

* Tighten comments

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Daniel Han 2026-07-27 02:14:20 -07:00 committed by GitHub
commit 3fd948eb95
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 563 additions and 114 deletions

View file

@ -44,7 +44,7 @@ def generate_bootstrap_password() -> str:
# Persisted from a previous run? # Persisted from a previous run?
if _BOOTSTRAP_PW_PATH.is_file(): if _BOOTSTRAP_PW_PATH.is_file():
_bootstrap_password = _BOOTSTRAP_PW_PATH.read_text().strip() _bootstrap_password = _BOOTSTRAP_PW_PATH.read_text(encoding = "utf-8").strip()
if _bootstrap_password: if _bootstrap_password:
return _bootstrap_password return _bootstrap_password
@ -57,7 +57,7 @@ def generate_bootstrap_password() -> str:
# Persist so the same passphrase survives restarts until password change. # Persist so the same passphrase survives restarts until password change.
ensure_dir(_BOOTSTRAP_PW_PATH.parent) ensure_dir(_BOOTSTRAP_PW_PATH.parent)
_BOOTSTRAP_PW_PATH.write_text(_bootstrap_password) _BOOTSTRAP_PW_PATH.write_text(_bootstrap_password, encoding = "utf-8")
try: try:
os.chmod(_BOOTSTRAP_PW_PATH, 0o600) os.chmod(_BOOTSTRAP_PW_PATH, 0o600)
except OSError: except OSError:
@ -76,7 +76,7 @@ def _load_bootstrap_password() -> Optional[str]:
global _bootstrap_password global _bootstrap_password
_bootstrap_password = None _bootstrap_password = None
if _BOOTSTRAP_PW_PATH.is_file(): if _BOOTSTRAP_PW_PATH.is_file():
bootstrap_password = _BOOTSTRAP_PW_PATH.read_text().strip() bootstrap_password = _BOOTSTRAP_PW_PATH.read_text(encoding = "utf-8").strip()
if bootstrap_password: if bootstrap_password:
_bootstrap_password = bootstrap_password _bootstrap_password = bootstrap_password
return _bootstrap_password return _bootstrap_password
@ -99,7 +99,7 @@ def clear_bootstrap_password() -> None:
# stale plaintext can't be re-seeded by generate_bootstrap_password() # stale plaintext can't be re-seeded by generate_bootstrap_password()
# if a later reset-password deletes auth.db and re-validates it. # if a later reset-password deletes auth.db and re-validates it.
try: try:
_BOOTSTRAP_PW_PATH.write_text("") _BOOTSTRAP_PW_PATH.write_text("", encoding = "utf-8")
cleared = True cleared = True
except OSError: except OSError:
cleared = False cleared = False

View file

@ -90,7 +90,7 @@ def _store_colab_login_credentials(username: str, password: str) -> None:
path = _colab_login_credentials_path() path = _colab_login_credentials_path()
try: try:
path.parent.mkdir(parents = True, exist_ok = True) path.parent.mkdir(parents = True, exist_ok = True)
path.write_text(f"{username}\n{password}\n") path.write_text(f"{username}\n{password}\n", encoding = "utf-8")
try: try:
import os import os
os.chmod(path, 0o600) os.chmod(path, 0o600)
@ -106,7 +106,7 @@ def _load_colab_login_credentials() -> "tuple[str, str] | None":
try: try:
if not path.is_file(): if not path.is_file():
return None return None
lines = path.read_text().splitlines() lines = path.read_text(encoding = "utf-8").splitlines()
if len(lines) >= 2 and lines[0] and lines[1]: if len(lines) >= 2 and lines[0] and lines[1]:
return lines[0], lines[1] return lines[0], lines[1]
except OSError as e: except OSError as e:

View file

@ -241,7 +241,7 @@ def _offline_window_if(local_files_only):
def _is_wsl(): def _is_wsl():
"""Detect if running under Windows Subsystem for Linux.""" """Detect if running under Windows Subsystem for Linux."""
try: try:
return "microsoft" in open("/proc/version").read().lower() return "microsoft" in open("/proc/version", encoding = "utf-8").read().lower()
except Exception: except Exception:
return False return False
@ -574,7 +574,7 @@ class ExportBackend:
) )
metadata = {"base_model": base_model} metadata = {"base_model": base_model}
metadata_path = os.path.join(save_directory, "export_metadata.json") metadata_path = os.path.join(save_directory, "export_metadata.json")
with open(metadata_path, "w") as f: with open(metadata_path, "w", encoding = "utf-8") as f:
json.dump(metadata, f, indent = 2) json.dump(metadata, f, indent = 2)
logger.info(f"Wrote export metadata to {metadata_path}") logger.info(f"Wrote export metadata to {metadata_path}")
except Exception as e: except Exception as e:

View file

@ -567,7 +567,7 @@ class InferenceBackend:
_meta_path = Path(config.path) / "export_metadata.json" _meta_path = Path(config.path) / "export_metadata.json"
try: try:
if _meta_path.exists(): if _meta_path.exists():
_meta = json.loads(_meta_path.read_text()) _meta = json.loads(_meta_path.read_text(encoding = "utf-8"))
if _meta.get("base_model"): if _meta.get("base_model"):
processor_source = _meta["base_model"] processor_source = _meta["base_model"]
except Exception: except Exception:

View file

@ -569,7 +569,7 @@ def _load_swa_cache() -> dict:
if _SWA_CACHE is not None: if _SWA_CACHE is not None:
return _SWA_CACHE return _SWA_CACHE
try: try:
with open(_swa_cache_path()) as f: with open(_swa_cache_path(), encoding = "utf-8") as f:
_SWA_CACHE = json.load(f) _SWA_CACHE = json.load(f)
if not isinstance(_SWA_CACHE, dict): if not isinstance(_SWA_CACHE, dict):
_SWA_CACHE = {} _SWA_CACHE = {}
@ -583,7 +583,7 @@ def _save_swa_cache(cache: dict) -> None:
path = _swa_cache_path() path = _swa_cache_path()
path.parent.mkdir(parents = True, exist_ok = True) path.parent.mkdir(parents = True, exist_ok = True)
tmp = path.with_suffix(".json.tmp") tmp = path.with_suffix(".json.tmp")
with open(tmp, "w") as f: with open(tmp, "w", encoding = "utf-8") as f:
json.dump(cache, f, indent = 2, sort_keys = True) json.dump(cache, f, indent = 2, sort_keys = True)
tmp.replace(path) tmp.replace(path)
except OSError: except OSError:
@ -620,7 +620,7 @@ def _fetch_swa_entry_from_hf(repo_id: str) -> Optional[object]:
repo_type = "model", repo_type = "model",
cache_dir = active_hf_hub_cache(), cache_dir = active_hf_hub_cache(),
) )
with open(cfg_path) as f: with open(cfg_path, encoding = "utf-8") as f:
cfg = json.load(f) cfg = json.load(f)
except Exception: except Exception:
return None return None
@ -3596,7 +3596,7 @@ class LlamaCppBackend:
except Exception: except Exception:
pass pass
try: try:
with open("/proc/meminfo") as f: with open("/proc/meminfo", encoding = "utf-8") as f:
for line in f: for line in f:
if line.startswith("MemAvailable:"): if line.startswith("MemAvailable:"):
return int(line.split()[1]) // 1024 # kB -> MiB return int(line.split()[1]) // 1024 # kB -> MiB
@ -9477,7 +9477,7 @@ class LlamaCppBackend:
return return
try: try:
path.parent.mkdir(parents = True, exist_ok = True) path.parent.mkdir(parents = True, exist_ok = True)
path.write_text(f"{pid}:{cls._pid_start_identity(pid)}") path.write_text(f"{pid}:{cls._pid_start_identity(pid)}", encoding = "utf-8")
except Exception as e: except Exception as e:
logger.debug(f"Could not write llama-server pidfile: {e}") logger.debug(f"Could not write llama-server pidfile: {e}")
@ -9611,7 +9611,7 @@ class LlamaCppBackend:
pid = -1 pid = -1
identity = "" identity = ""
try: try:
pid_str, _, identity = path.read_text().strip().partition(":") pid_str, _, identity = path.read_text(encoding = "utf-8").strip().partition(":")
pid = int(pid_str) pid = int(pid_str)
except Exception: except Exception:
pid = -1 pid = -1

View file

@ -111,7 +111,7 @@ def _load_sidecar(cwd):
"""Return the persisted ``source -> healed target`` map, or {} on any error """Return the persisted ``source -> healed target`` map, or {} on any error
(missing/corrupt/foreign sidecar degrades to in-process-only behaviour).""" (missing/corrupt/foreign sidecar degrades to in-process-only behaviour)."""
try: try:
with open(_sidecar_path(cwd)) as fh: with open(_sidecar_path(cwd), encoding = "utf-8") as fh:
data = json.load(fh) data = json.load(fh)
except Exception: # noqa: BLE001 - a bad sidecar must never break user code except Exception: # noqa: BLE001 - a bad sidecar must never break user code
return {} return {}
@ -131,7 +131,7 @@ def _record_sidecar(cwd, source, target):
return return
data[source] = target data[source] = target
tmp = _sidecar_path(cwd) + ".tmp" tmp = _sidecar_path(cwd) + ".tmp"
with open(tmp, "w") as fh: with open(tmp, "w", encoding = "utf-8") as fh:
json.dump(data, fh) json.dump(data, fh)
os.replace(tmp, _sidecar_path(cwd)) os.replace(tmp, _sidecar_path(cwd))
except Exception: # noqa: BLE001 - persistence is best effort only except Exception: # noqa: BLE001 - persistence is best effort only

View file

@ -151,7 +151,7 @@ def _resolve_lora_4bit(mc, load_in_4bit: bool) -> bool:
import json import json
try: try:
with open(adapter_cfg_path) as f: with open(adapter_cfg_path, encoding = "utf-8") as f:
adapter_cfg = json.load(f) adapter_cfg = json.load(f)
training_method = adapter_cfg.get("unsloth_training_method") training_method = adapter_cfg.get("unsloth_training_method")
if training_method == "lora" and load_in_4bit: if training_method == "lora" and load_in_4bit:
@ -961,7 +961,10 @@ def run_inference_process(
if _local_adapter_cfg.is_file(): if _local_adapter_cfg.is_file():
try: try:
_lora_base = ( _lora_base = (
_json.loads(_local_adapter_cfg.read_text()).get("base_model_name_or_path") or None _json.loads(_local_adapter_cfg.read_text(encoding = "utf-8")).get(
"base_model_name_or_path"
)
or None
) )
except Exception: except Exception:
_lora_base = None _lora_base = None

View file

@ -100,7 +100,7 @@ def _st_module_subdirs(name: str, token: str | None) -> tuple[str, ...]:
path = Path(normalize_path(name)).expanduser() / "modules.json" path = Path(normalize_path(name)).expanduser() / "modules.json"
if not path.is_file(): if not path.is_file():
return () return ()
data = json.loads(path.read_text()) data = json.loads(path.read_text(encoding = "utf-8"))
else: else:
from huggingface_hub import hf_hub_download from huggingface_hub import hf_hub_download
from huggingface_hub.utils import EntryNotFoundError from huggingface_hub.utils import EntryNotFoundError
@ -115,7 +115,7 @@ def _st_module_subdirs(name: str, token: str | None) -> tuple[str, ...]:
) )
except EntryNotFoundError: except EntryNotFoundError:
return () return ()
data = json.loads(open(local).read()) data = json.loads(open(local, encoding = "utf-8").read())
subdirs = [] subdirs = []
for module in data or (): for module in data or ():
sub = str((module or {}).get("path", "")).strip().strip("/") sub = str((module or {}).get("path", "")).strip().strip("/")

View file

@ -215,7 +215,7 @@ def _ollama_model_info_from_manifest(
return None return None
try: try:
manifest = json.loads(tag_file.read_text()) manifest = json.loads(tag_file.read_text(encoding = "utf-8"))
except (json.JSONDecodeError, OSError) as e: except (json.JSONDecodeError, OSError) as e:
logger.debug("Skipping unreadable/invalid Ollama manifest %s: %s", tag_file, e) logger.debug("Skipping unreadable/invalid Ollama manifest %s: %s", tag_file, e)
return None return None
@ -228,7 +228,7 @@ def _ollama_model_info_from_manifest(
config_blob = _ollama_blob_path(blobs_dir, config_digest) config_blob = _ollama_blob_path(blobs_dir, config_digest)
if config_blob is not None and _safe_is_file(config_blob): if config_blob is not None and _safe_is_file(config_blob):
try: try:
cfg = json.loads(config_blob.read_text()) cfg = json.loads(config_blob.read_text(encoding = "utf-8"))
model_type = cfg.get("model_type", "") model_type = cfg.get("model_type", "")
file_type = cfg.get("file_type", "") file_type = cfg.get("file_type", "")
except (json.JSONDecodeError, OSError) as e: except (json.JSONDecodeError, OSError) as e:

View file

@ -462,7 +462,7 @@ def _read_marker_value(marker: Path) -> Optional[str]:
try: try:
if not marker.exists(): if not marker.exists():
return None return None
value = marker.read_text().strip() value = marker.read_text(encoding = "utf-8").strip()
except OSError: except OSError:
return None return None
return value if value in VALID_TRANSPORTS else None return value if value in VALID_TRANSPORTS else None
@ -473,7 +473,7 @@ def _write_marker_value(marker: Path, mode: str) -> None:
# tmp + rename so a SIGKILL mid-write can't leave a half-written marker. # tmp + rename so a SIGKILL mid-write can't leave a half-written marker.
# The tmp name is per-process so concurrent writers don't clobber tmps. # The tmp name is per-process so concurrent writers don't clobber tmps.
tmp = marker.with_name(f"{marker.name}.tmp-{os.getpid()}") tmp = marker.with_name(f"{marker.name}.tmp-{os.getpid()}")
tmp.write_text(mode) tmp.write_text(mode, encoding = "utf-8")
os.replace(tmp, marker) os.replace(tmp, marker)
except OSError: except OSError:
# Best-effort: a missing marker next run purges the partial defensively, # Best-effort: a missing marker next run purges the partial defensively,

View file

@ -103,7 +103,7 @@ def _is_wsl() -> bool:
if sys.platform == "win32": if sys.platform == "win32":
return False return False
try: try:
return "microsoft" in Path("/proc/version").read_text().lower() return "microsoft" in Path("/proc/version").read_text(encoding = "utf-8").lower()
except Exception: except Exception:
return False return False
@ -124,7 +124,7 @@ def _wsl_automount_root() -> str:
import configparser import configparser
parser = configparser.ConfigParser(inline_comment_prefixes = ("#", ";")) parser = configparser.ConfigParser(inline_comment_prefixes = ("#", ";"))
parser.read("/etc/wsl.conf") parser.read("/etc/wsl.conf", encoding = "utf-8")
root = parser.get("automount", "root", fallback = "").strip().strip("\"'") root = parser.get("automount", "root", fallback = "").strip().strip("\"'")
except Exception: except Exception:
return default return default

View file

@ -254,7 +254,11 @@ def _read_studio_install_id() -> str:
/api/health emits "" and the launcher accepts any healthy backend. /api/health emits "" and the launcher accepts any healthy backend.
Carries no install-path info (matters when Unsloth runs -H 0.0.0.0).""" Carries no install-path info (matters when Unsloth runs -H 0.0.0.0)."""
try: try:
token = (_STUDIO_ROOT_RESOLVED / "share" / "studio_install_id").read_text().strip() token = (
(_STUDIO_ROOT_RESOLVED / "share" / "studio_install_id")
.read_text(encoding = "utf-8")
.strip()
)
except (OSError, ValueError): except (OSError, ValueError):
return "" return ""
return token if _STUDIO_INSTALL_ID_RE.fullmatch(token) else "" return token if _STUDIO_INSTALL_ID_RE.fullmatch(token) else ""

View file

@ -20,7 +20,7 @@ class StateStore:
self._data: Dict[str, Any] = {} self._data: Dict[str, Any] = {}
if self.path.exists(): if self.path.exists():
try: try:
with self.path.open() as f: with self.path.open(encoding = "utf-8") as f:
self._data = json.load(f) self._data = json.load(f)
except Exception: except Exception:
self._data = {} self._data = {}
@ -51,7 +51,7 @@ class StateStore:
def _flush(self) -> None: def _flush(self) -> None:
tmp = self.path.with_suffix(self.path.suffix + ".tmp") tmp = self.path.with_suffix(self.path.suffix + ".tmp")
with tmp.open("w") as f: with tmp.open("w", encoding = "utf-8") as f:
json.dump(self._data, f, indent = 2, default = str) json.dump(self._data, f, indent = 2, default = str)
os.replace(tmp, self.path) os.replace(tmp, self.path)
@ -63,12 +63,14 @@ class JsonlWriter:
self.path = Path(path) self.path = Path(path)
self.path.parent.mkdir(parents = True, exist_ok = True) self.path.parent.mkdir(parents = True, exist_ok = True)
self._lock = threading.Lock() self._lock = threading.Lock()
self._fh = self.path.open("a", buffering = 1) self._fh = self.path.open("a", buffering = 1, encoding = "utf-8")
self._count_seen_keys: set[str] = set() self._count_seen_keys: set[str] = set()
# Preload seen keys for dedup across resumes # Preload seen keys for dedup across resumes
if self.path.exists() and self.path.stat().st_size > 0: if self.path.exists() and self.path.stat().st_size > 0:
try: try:
with self.path.open() as f: # No guess is safe for a file an older build wrote in the
# operator's locale, so read past whatever will not decode.
with self.path.open(encoding = "utf-8", errors = "replace") as f:
for line in f: for line in f:
try: try:
obj = json.loads(line) obj = json.loads(line)

View file

@ -27,7 +27,7 @@ class UnstructuredSeedReader(SeedReader[UnstructuredSeedSource]):
orig_name = path_obj.name orig_name = path_obj.name
if meta_path.exists(): if meta_path.exists():
try: try:
meta = json_mod.loads(meta_path.read_text()) meta = json_mod.loads(meta_path.read_text(encoding = "utf-8"))
orig_name = meta.get("original_filename", path_obj.name) orig_name = meta.get("original_filename", path_obj.name)
except (json_mod.JSONDecodeError, OSError): except (json_mod.JSONDecodeError, OSError):
pass pass

View file

@ -3778,7 +3778,7 @@ def _effective_load_in_4bit(config: ModelConfig, requested: bool) -> bool:
if not adapter_cfg_path.exists(): if not adapter_cfg_path.exists():
return load_in_4bit return load_in_4bit
try: try:
with open(adapter_cfg_path) as f: with open(adapter_cfg_path, encoding = "utf-8") as f:
adapter_cfg = json.load(f) adapter_cfg = json.load(f)
if not isinstance(adapter_cfg, dict): # malformed -> keep requested if not isinstance(adapter_cfg, dict): # malformed -> keep requested
return load_in_4bit return load_in_4bit

View file

@ -722,7 +722,7 @@ def _scan_ollama_dir(ollama_dir: Path, limit: Optional[int] = None) -> List[Loca
stem_hash = hashlib.sha256(manifest_key.encode()).hexdigest()[:10] stem_hash = hashlib.sha256(manifest_key.encode()).hexdigest()[:10]
try: try:
manifest = json.loads(tag_file.read_text()) manifest = json.loads(tag_file.read_text(encoding = "utf-8"))
except (json.JSONDecodeError, OSError) as e: except (json.JSONDecodeError, OSError) as e:
logger.debug( logger.debug(
"Skipping unreadable/invalid Ollama manifest %s: %s", "Skipping unreadable/invalid Ollama manifest %s: %s",
@ -738,7 +738,7 @@ def _scan_ollama_dir(ollama_dir: Path, limit: Optional[int] = None) -> List[Loca
config_blob = blobs_dir / config_digest.replace(":", "-") config_blob = blobs_dir / config_digest.replace(":", "-")
if config_blob.is_file(): if config_blob.is_file():
try: try:
cfg = json.loads(config_blob.read_text()) cfg = json.loads(config_blob.read_text(encoding = "utf-8"))
model_type = cfg.get("model_type", "") model_type = cfg.get("model_type", "")
file_type = cfg.get("file_type", "") file_type = cfg.get("file_type", "")
except (json.JSONDecodeError, OSError) as e: except (json.JSONDecodeError, OSError) as e:
@ -1042,7 +1042,7 @@ def _dir_has_downloaded_model(directory: Path, max_entries: int = 4000) -> bool:
if not m.is_file(): if not m.is_file():
continue continue
try: try:
manifest = json.loads(m.read_text()) manifest = json.loads(m.read_text(encoding = "utf-8"))
except (json.JSONDecodeError, OSError, ValueError): except (json.JSONDecodeError, OSError, ValueError):
continue continue
for layer in manifest.get("layers") or []: for layer in manifest.get("layers") or []:

View file

@ -774,7 +774,7 @@ def _write_pid_file():
"""Write the current process PID to the studio PID file.""" """Write the current process PID to the studio PID file."""
try: try:
_PID_FILE.parent.mkdir(parents = True, exist_ok = True) _PID_FILE.parent.mkdir(parents = True, exist_ok = True)
_PID_FILE.write_text(str(os.getpid())) _PID_FILE.write_text(str(os.getpid()), encoding = "utf-8")
except OSError: except OSError:
pass pass
@ -783,7 +783,7 @@ def _remove_pid_file():
"""Remove the PID file if it belongs to this process.""" """Remove the PID file if it belongs to this process."""
try: try:
if _PID_FILE.is_file(): if _PID_FILE.is_file():
stored = _PID_FILE.read_text().strip() stored = _PID_FILE.read_text(encoding = "utf-8").strip()
if stored == str(os.getpid()): if stored == str(os.getpid()):
_PID_FILE.unlink(missing_ok = True) _PID_FILE.unlink(missing_ok = True)
except OSError: except OSError:

View file

@ -776,7 +776,7 @@ def _rocm_linux_sysfs_gpu_busy_pct() -> Optional[float]:
files = glob.glob("/sys/class/drm/card*/device/gpu_busy_percent") files = glob.glob("/sys/class/drm/card*/device/gpu_busy_percent")
if not files: if not files:
return None return None
values = [int(open(f).read().strip()) for f in files] values = [int(open(f, encoding = "utf-8").read().strip()) for f in files]
return round(sum(values) / len(values), 1) return round(sum(values) / len(values), 1)
except Exception: except Exception:
return None return None
@ -790,7 +790,7 @@ def _rocm_linux_sysfs_temp_c() -> Optional[float]:
files = glob.glob("/sys/class/drm/card*/device/hwmon/hwmon*/temp1_input") files = glob.glob("/sys/class/drm/card*/device/hwmon/hwmon*/temp1_input")
if not files: if not files:
return None return None
temps = [int(open(f).read().strip()) / 1000.0 for f in files] temps = [int(open(f, encoding = "utf-8").read().strip()) / 1000.0 for f in files]
return round(max(temps), 1) return round(max(temps), 1)
except Exception: except Exception:
return None return None
@ -807,7 +807,9 @@ def _rocm_linux_sysfs_power_w() -> Optional[float]:
): ):
files = glob.glob(pattern) files = glob.glob(pattern)
if files: if files:
watts = sum(int(open(f).read().strip()) / 1_000_000.0 for f in files) watts = sum(
int(open(f, encoding = "utf-8").read().strip()) / 1_000_000.0 for f in files
)
return round(watts, 1) return round(watts, 1)
return None return None
except Exception: except Exception:
@ -852,8 +854,8 @@ def _rocm_linux_sysfs_vram_gb() -> tuple[Optional[float], Optional[float]]:
total_files = glob.glob("/sys/class/drm/card*/device/mem_info_vram_total") total_files = glob.glob("/sys/class/drm/card*/device/mem_info_vram_total")
if not used_files or not total_files: if not used_files or not total_files:
return None, None return None, None
used_bytes = sum(int(open(f).read().strip()) for f in used_files) used_bytes = sum(int(open(f, encoding = "utf-8").read().strip()) for f in used_files)
total_bytes = sum(int(open(f).read().strip()) for f in total_files) total_bytes = sum(int(open(f, encoding = "utf-8").read().strip()) for f in total_files)
if total_bytes == 0: if total_bytes == 0:
return None, None return None, None
return round(used_bytes / (1024**3), 2), round(total_bytes / (1024**3), 2) return round(used_bytes / (1024**3), 2), round(total_bytes / (1024**3), 2)
@ -893,7 +895,7 @@ def _rocm_kfd_gpu_pci_ids() -> list[str]:
continue continue
props: dict[str, int] = {} props: dict[str, int] = {}
try: try:
with open(os.path.join(node_dir, "properties")) as f: with open(os.path.join(node_dir, "properties"), encoding = "utf-8") as f:
for line in f: for line in f:
parts = line.split() parts = line.split()
if len(parts) == 2: if len(parts) == 2:
@ -979,9 +981,9 @@ def _rocm_linux_sysfs_vram_by_pci_gb() -> dict[str, tuple[float, float]]:
if not bdf: if not bdf:
continue continue
try: try:
with open(os.path.join(dev_dir, "mem_info_vram_used")) as f: with open(os.path.join(dev_dir, "mem_info_vram_used"), encoding = "utf-8") as f:
used_bytes = int(f.read().strip()) used_bytes = int(f.read().strip())
with open(os.path.join(dev_dir, "mem_info_vram_total")) as f: with open(os.path.join(dev_dir, "mem_info_vram_total"), encoding = "utf-8") as f:
total_bytes = int(f.read().strip()) total_bytes = int(f.read().strip())
except (OSError, ValueError): except (OSError, ValueError):
continue continue

View file

@ -129,7 +129,7 @@ def _read_checkpoint_loss(checkpoint_path: Path) -> Optional[float]:
if not trainer_state.exists(): if not trainer_state.exists():
return None return None
try: try:
with open(trainer_state) as f: with open(trainer_state, encoding = "utf-8") as f:
state = json.load(f) state = json.load(f)
log_history = state.get("log_history", []) log_history = state.get("log_history", [])
if log_history: if log_history:
@ -174,18 +174,18 @@ def scan_checkpoints(
metadata: dict = {} metadata: dict = {}
try: try:
if adapter_config.exists(): if adapter_config.exists():
cfg = json.loads(adapter_config.read_text()) cfg = json.loads(adapter_config.read_text(encoding = "utf-8"))
metadata["base_model"] = cfg.get("base_model_name_or_path") metadata["base_model"] = cfg.get("base_model_name_or_path")
metadata["peft_type"] = cfg.get("peft_type") metadata["peft_type"] = cfg.get("peft_type")
metadata["lora_rank"] = cfg.get("r") metadata["lora_rank"] = cfg.get("r")
elif config_file.exists(): elif config_file.exists():
cfg = json.loads(config_file.read_text()) cfg = json.loads(config_file.read_text(encoding = "utf-8"))
metadata["base_model"] = cfg.get("_name_or_path") metadata["base_model"] = cfg.get("_name_or_path")
# Detect BNB quantization from config.json # Detect BNB quantization from config.json
if config_file.exists(): if config_file.exists():
if "cfg" not in dir(): if "cfg" not in dir():
cfg = json.loads(config_file.read_text()) cfg = json.loads(config_file.read_text(encoding = "utf-8"))
quant_cfg = cfg.get("quantization_config") quant_cfg = cfg.get("quantization_config")
if ( if (
isinstance(quant_cfg, dict) isinstance(quant_cfg, dict)

View file

@ -631,7 +631,7 @@ def _raw_config_has_vision_config(
cache_dir = active_hf_hub_cache(), cache_dir = active_hf_hub_cache(),
) )
) )
config = json.loads(config_path.read_text()) config = json.loads(config_path.read_text(encoding = "utf-8"))
architectures = config.get("architectures") or [] architectures = config.get("architectures") or []
model_type = config.get("model_type") model_type = config.get("model_type")
explicit_vision = ( explicit_vision = (
@ -1083,7 +1083,7 @@ def _detect_audio_from_tokenizer(
]: ]:
tok_file = snapshot / tok_path tok_file = snapshot / tok_path
if tok_file.exists(): if tok_file.exists():
tok_config = json.loads(tok_file.read_text()) tok_config = json.loads(tok_file.read_text(encoding = "utf-8"))
read_any = True read_any = True
result = _check_token_patterns(tok_config) result = _check_token_patterns(tok_config)
if result: if result:
@ -2283,7 +2283,7 @@ def scan_exported_models(
export_meta = run_dir / "export_metadata.json" export_meta = run_dir / "export_metadata.json"
try: try:
if export_meta.exists(): if export_meta.exists():
meta = json.loads(export_meta.read_text()) meta = json.loads(export_meta.read_text(encoding = "utf-8"))
base_model = meta.get("base_model") base_model = meta.get("base_model")
except Exception: except Exception:
pass pass
@ -2312,7 +2312,7 @@ def scan_exported_models(
if adapter_config.exists(): if adapter_config.exists():
export_type = "lora" export_type = "lora"
try: try:
cfg = json.loads(adapter_config.read_text()) cfg = json.loads(adapter_config.read_text(encoding = "utf-8"))
base_model = cfg.get("base_model_name_or_path") base_model = cfg.get("base_model_name_or_path")
except Exception: except Exception:
pass pass
@ -2321,7 +2321,7 @@ def scan_exported_models(
export_meta = checkpoint_dir / "export_metadata.json" export_meta = checkpoint_dir / "export_metadata.json"
try: try:
if export_meta.exists(): if export_meta.exists():
meta = json.loads(export_meta.read_text()) meta = json.loads(export_meta.read_text(encoding = "utf-8"))
base_model = meta.get("base_model") base_model = meta.get("base_model")
except Exception: except Exception:
pass pass
@ -2334,7 +2334,7 @@ def scan_exported_models(
export_meta = meta_dir / "export_metadata.json" export_meta = meta_dir / "export_metadata.json"
try: try:
if export_meta.exists(): if export_meta.exists():
meta = json.loads(export_meta.read_text()) meta = json.loads(export_meta.read_text(encoding = "utf-8"))
base_model = meta.get("base_model") base_model = meta.get("base_model")
if base_model: if base_model:
break break
@ -2354,7 +2354,7 @@ def scan_exported_models(
outputs_adapter_cfg = resolve_output_dir(run_dir.name) / "adapter_config.json" outputs_adapter_cfg = resolve_output_dir(run_dir.name) / "adapter_config.json"
try: try:
if outputs_adapter_cfg.exists(): if outputs_adapter_cfg.exists():
cfg = json.loads(outputs_adapter_cfg.read_text()) cfg = json.loads(outputs_adapter_cfg.read_text(encoding = "utf-8"))
base_model = cfg.get("base_model_name_or_path") base_model = cfg.get("base_model_name_or_path")
except Exception: except Exception:
pass pass
@ -2380,7 +2380,7 @@ def get_base_model_from_checkpoint(checkpoint_path: str) -> Optional[str]:
adapter_config_path = checkpoint_path_obj / "adapter_config.json" adapter_config_path = checkpoint_path_obj / "adapter_config.json"
if adapter_config_path.exists(): if adapter_config_path.exists():
with open(adapter_config_path, "r") as f: with open(adapter_config_path, "r", encoding = "utf-8") as f:
config = json.load(f) config = json.load(f)
base_model = config.get("base_model_name_or_path") base_model = config.get("base_model_name_or_path")
if base_model: if base_model:
@ -2389,7 +2389,7 @@ def get_base_model_from_checkpoint(checkpoint_path: str) -> Optional[str]:
config_path = checkpoint_path_obj / "config.json" config_path = checkpoint_path_obj / "config.json"
if config_path.exists(): if config_path.exists():
with open(config_path, "r") as f: with open(config_path, "r", encoding = "utf-8") as f:
config = json.load(f) config = json.load(f)
for key in ("model_name", "_name_or_path"): for key in ("model_name", "_name_or_path"):
base_model = config.get(key) base_model = config.get(key)
@ -2445,7 +2445,7 @@ def get_base_model_from_lora(lora_path: str) -> Optional[str]:
# adapter_config.json first # adapter_config.json first
adapter_config_path = lora_path_obj / "adapter_config.json" adapter_config_path = lora_path_obj / "adapter_config.json"
if adapter_config_path.exists(): if adapter_config_path.exists():
with open(adapter_config_path, "r") as f: with open(adapter_config_path, "r", encoding = "utf-8") as f:
config = json.load(f) config = json.load(f)
base_model = config.get("base_model_name_or_path") base_model = config.get("base_model_name_or_path")
if base_model: if base_model:
@ -2535,7 +2535,7 @@ def get_base_model_from_lora_identifier(
last_exc = exc last_exc = exc
continue continue
try: try:
with open(cfg_path, "r") as f: with open(cfg_path, "r", encoding = "utf-8") as f:
base_model = json.load(f).get("base_model_name_or_path") base_model = json.load(f).get("base_model_name_or_path")
except Exception as exc: except Exception as exc:
logger.warning("Could not parse adapter_config.json for '%s': %s", identifier, exc) logger.warning("Could not parse adapter_config.json for '%s': %s", identifier, exc)
@ -2781,7 +2781,7 @@ class ModelConfig:
meta_path = gguf_dir / "export_metadata.json" meta_path = gguf_dir / "export_metadata.json"
if meta_path.exists(): if meta_path.exists():
try: try:
meta = json.loads(meta_path.read_text()) meta = json.loads(meta_path.read_text(encoding = "utf-8"))
base = meta.get("base_model") base = meta.get("base_model")
if base and is_vision_model(base, hf_token = hf_token): if base and is_vision_model(base, hf_token = hf_token):
base_is_vision = True base_is_vision = True
@ -2912,7 +2912,7 @@ class ModelConfig:
token = hf_token, token = hf_token,
cache_dir = active_hf_hub_cache(), cache_dir = active_hf_hub_cache(),
) )
with open(config_path, "r") as f: with open(config_path, "r", encoding = "utf-8") as f:
adapter_config = json.load(f) adapter_config = json.load(f)
base_model = adapter_config.get("base_model_name_or_path") base_model = adapter_config.get("base_model_name_or_path")
if base_model: if base_model:

View file

@ -34,7 +34,7 @@ def _is_wsl() -> bool:
if sys.platform == "win32": if sys.platform == "win32":
return False return False
try: try:
with open("/proc/version", "r") as f: with open("/proc/version", "r", encoding = "utf-8") as f:
return "microsoft" in f.read().lower() return "microsoft" in f.read().lower()
except Exception: except Exception:
return False return False

View file

@ -212,7 +212,7 @@ def lmstudio_model_dirs() -> list[Path]:
settings_path = Path.home() / ".lmstudio" / "settings.json" settings_path = Path.home() / ".lmstudio" / "settings.json"
if settings_path.is_file(): if settings_path.is_file():
try: try:
with open(settings_path) as f: with open(settings_path, encoding = "utf-8") as f:
settings = json.load(f) settings = json.load(f)
downloads = settings.get("downloadsFolder", "") downloads = settings.get("downloadsFolder", "")
if downloads: if downloads:

View file

@ -142,7 +142,7 @@ def _load_remote_code_configs(model_name: str, hf_token: Optional[str] = None) -
for name in _REMOTE_CODE_CONFIG_FILES: for name in _REMOTE_CODE_CONFIG_FILES:
p = root / name p = root / name
if p.is_file(): if p.is_file():
configs.append(json.loads(p.read_text())) configs.append(json.loads(p.read_text(encoding = "utf-8")))
return configs return configs
from huggingface_hub import hf_hub_download from huggingface_hub import hf_hub_download
@ -164,7 +164,7 @@ def _load_remote_code_configs(model_name: str, hf_token: Optional[str] = None) -
# Transient/auth failure is not "absent" -> fail closed to "unknown" so # Transient/auth failure is not "absent" -> fail closed to "unknown" so
# the caller scans (a tokenizer/processor-only auto_map must not slip by). # the caller scans (a tokenizer/processor-only auto_map must not slip by).
return None return None
configs.append(json.loads(Path(p).read_text())) configs.append(json.loads(Path(p).read_text(encoding = "utf-8")))
# Every config was read or a genuine 404 -> an empty list is a definitive # Every config was read or a genuine 404 -> an empty list is a definitive
# "no auto_map", not "unknown". # "no auto_map", not "unknown".
return configs return configs

View file

@ -199,7 +199,9 @@ def _indexed_shard_paths(
inconclusive = True # transient: an index that might exist could not be read inconclusive = True # transient: an index that might exist could not be read
continue continue
try: try:
weight_map = (json.loads(open(index_path).read()) or {}).get("weight_map") or {} weight_map = (json.loads(open(index_path, encoding = "utf-8").read()) or {}).get(
"weight_map"
) or {}
for shard in weight_map.values(): for shard in weight_map.values():
shard_norm = _normalize_repo_path(str(shard)) shard_norm = _normalize_repo_path(str(shard))
# weight_map paths are relative to the index file's directory. # weight_map paths are relative to the index file's directory.
@ -326,7 +328,7 @@ def _st_load_roots(snapshot: Path) -> list:
roots = [snapshot] roots = [snapshot]
try: try:
import json import json
modules = json.loads((snapshot / "modules.json").read_text()) modules = json.loads((snapshot / "modules.json").read_text(encoding = "utf-8"))
except (OSError, ValueError): except (OSError, ValueError):
return roots # no / invalid modules.json -> snapshot root is the only load root return roots # no / invalid modules.json -> snapshot root is the only load root
for module in modules or (): for module in modules or ():

View file

@ -69,7 +69,7 @@ def approval_target_key(targets) -> str:
def _load() -> dict: def _load() -> dict:
"""Parsed store, or an empty skeleton on any error (fail-safe = re-prompt).""" """Parsed store, or an empty skeleton on any error (fail-safe = re-prompt)."""
try: try:
with open(_store_path()) as f: with open(_store_path(), encoding = "utf-8") as f:
data = json.load(f) data = json.load(f)
# Validate the shape, not just the version: a hand-edited ``subjects`` that is not a # Validate the shape, not just the version: a hand-edited ``subjects`` that is not a
# dict (e.g. ``[]``) would otherwise crash lookup/record instead of failing safe. # dict (e.g. ``[]``) would otherwise crash lookup/record instead of failing safe.
@ -92,7 +92,7 @@ def _save(data: dict) -> None:
storage_roots.ensure_dir(path.parent) storage_roots.ensure_dir(path.parent)
tmp = path.parent / f".{path.name}.tmp-{os.getpid()}" tmp = path.parent / f".{path.name}.tmp-{os.getpid()}"
try: try:
with open(tmp, "w") as f: with open(tmp, "w", encoding = "utf-8") as f:
json.dump(data, f, indent = 2) json.dump(data, f, indent = 2)
try: try:
os.chmod(tmp, 0o600) os.chmod(tmp, 0o600)

View file

@ -21,6 +21,8 @@ canonical scanner loads in-repo so the fallback never silently takes over.
from __future__ import annotations from __future__ import annotations
import hashlib import hashlib
import io
import tokenize
import importlib.util import importlib.util
import pathlib import pathlib
import re import re
@ -392,6 +394,18 @@ def scan_remote_code_files(files: dict[str, str]) -> ScanResult:
return result return result
def _read_python_source(path) -> str:
"""Decode a .py the way Python will execute it: a PEP 263 cookie
(`# coding: cp1252`) wins, so forcing utf-8 would scan something other than
what runs."""
data = path.read_bytes()
try:
encoding = tokenize.detect_encoding(io.BytesIO(data).readline)[0]
except (SyntaxError, ValueError):
encoding = "utf-8"
return data.decode(encoding, errors = "replace")
def remote_code_fingerprint(files: dict[str, str]) -> str: def remote_code_fingerprint(files: dict[str, str]) -> str:
"""Stable sha256 over the (sorted) file contents, for pinning consent.""" """Stable sha256 over the (sorted) file contents, for pinning consent."""
h = hashlib.sha256() h = hashlib.sha256()
@ -430,7 +444,7 @@ def repo_remote_code_files(model_name: str, hf_token: Optional[str] = None) -> d
# for an RCE gate (HIGH stays approvable; only CRITICAL hard-blocks). # for an RCE gate (HIGH stays approvable; only CRITICAL hard-blocks).
for p in root.rglob("*.py"): for p in root.rglob("*.py"):
if p.is_file(): if p.is_file():
files[str(p.relative_to(root))] = p.read_text(errors = "replace") files[str(p.relative_to(root))] = _read_python_source(p)
# A local config can still point auto_map at an EXTERNAL Hub repo # A local config can still point auto_map at an EXTERNAL Hub repo
# (owner/name--module.Class) that executes on load, so fetch it. Every config # (owner/name--module.Class) that executes on load, so fetch it. Every config
# that can declare auto_map is checked, so a custom processor's external code # that can declare auto_map is checked, so a custom processor's external code
@ -440,7 +454,7 @@ def repo_remote_code_files(model_name: str, hf_token: Optional[str] = None) -> d
p = root / name p = root / name
if p.is_file(): if p.is_file():
try: try:
ext_refs |= _auto_map_refs(json.loads(p.read_text())) ext_refs |= _auto_map_refs(json.loads(p.read_text(encoding = "utf-8")))
except Exception: except Exception:
pass pass
if not _add_external_refs(files, ext_refs, hf_token, model_name): if not _add_external_refs(files, ext_refs, hf_token, model_name):
@ -469,7 +483,7 @@ def repo_remote_code_files(model_name: str, hf_token: Optional[str] = None) -> d
f"{model_name}: config {cfg_name} could not be fetched ({exc})" f"{model_name}: config {cfg_name} could not be fetched ({exc})"
) from exc ) from exc
try: try:
refs |= _auto_map_refs(json.loads(Path(cfg_path).read_text())) refs |= _auto_map_refs(json.loads(Path(cfg_path).read_text(encoding = "utf-8")))
except Exception: except Exception:
pass pass
own_refs = {fn for repo, fn in refs if repo is None} own_refs = {fn for repo, fn in refs if repo is None}
@ -519,7 +533,7 @@ def repo_remote_code_files(model_name: str, hf_token: Optional[str] = None) -> d
raise RemoteCodeUnscannable( raise RemoteCodeUnscannable(
f"{model_name}: present file {fn} could not be fetched ({exc})" f"{model_name}: present file {fn} could not be fetched ({exc})"
) from exc ) from exc
files[fn] = Path(fp).read_text(errors = "replace") files[fn] = _read_python_source(Path(fp))
# Code referenced from another repo executes too: scan it or fail closed. # Code referenced from another repo executes too: scan it or fail closed.
if not _add_external_refs(files, refs, hf_token, model_name): if not _add_external_refs(files, refs, hf_token, model_name):
raise RemoteCodeUnscannable(f"{model_name}: external auto_map code unreachable") raise RemoteCodeUnscannable(f"{model_name}: external auto_map code unreachable")
@ -602,7 +616,7 @@ def external_auto_map_repos(model_name: str, hf_token: Optional[str] = None) ->
if not p.is_file(): if not p.is_file():
continue continue
try: try:
refs = _auto_map_refs(json.loads(p.read_text())) refs = _auto_map_refs(json.loads(p.read_text(encoding = "utf-8")))
except Exception: except Exception:
continue continue
repos.update(repo for repo, _fn in refs if repo) repos.update(repo for repo, _fn in refs if repo)
@ -624,7 +638,7 @@ def external_auto_map_repos(model_name: str, hf_token: Optional[str] = None) ->
except Exception: except Exception:
continue continue
try: try:
refs = _auto_map_refs(json.loads(Path(cfg_path).read_text())) refs = _auto_map_refs(json.loads(Path(cfg_path).read_text(encoding = "utf-8")))
except Exception: except Exception:
continue continue
repos.update(repo for repo, _fn in refs if repo) repos.update(repo for repo, _fn in refs if repo)
@ -701,5 +715,5 @@ def _add_external_refs(files: dict, refs, hf_token, model_name: str) -> bool:
exc, exc,
) )
return False return False
files[f"{repo}--{fn}"] = Path(fp).read_text(errors = "replace") files[f"{repo}--{fn}"] = _read_python_source(Path(fp))
return True return True

View file

@ -420,7 +420,7 @@ def _resolve_base_model(model_name: str) -> str:
adapter_cfg_path = local_path / "adapter_config.json" adapter_cfg_path = local_path / "adapter_config.json"
if _safe_is_file(adapter_cfg_path): if _safe_is_file(adapter_cfg_path):
try: try:
with open(adapter_cfg_path) as f: with open(adapter_cfg_path, encoding = "utf-8") as f:
cfg = json.load(f) cfg = json.load(f)
base = cfg.get("base_model_name_or_path") base = cfg.get("base_model_name_or_path")
if base: if base:
@ -437,7 +437,7 @@ def _resolve_base_model(model_name: str) -> str:
config_json_path = local_path / "config.json" config_json_path = local_path / "config.json"
if _safe_is_file(config_json_path): if _safe_is_file(config_json_path):
try: try:
with open(config_json_path) as f: with open(config_json_path, encoding = "utf-8") as f:
cfg = json.load(f) cfg = json.load(f)
# Unsloth writes model_name, HF writes _name_or_path; skip a self-reference. # Unsloth writes model_name, HF writes _name_or_path; skip a self-reference.
for _key in ("model_name", "_name_or_path"): for _key in ("model_name", "_name_or_path"):
@ -534,14 +534,19 @@ def _adapter_base_from_hf_cache(model_name: str) -> str | None:
try: try:
if ref_main.is_file(): if ref_main.is_file():
candidates.append( candidates.append(
repo_dir / "snapshots" / ref_main.read_text().strip() / "adapter_config.json" repo_dir
/ "snapshots"
/ ref_main.read_text(encoding = "utf-8").strip()
/ "adapter_config.json"
) )
candidates += sorted( candidates += sorted(
repo_dir.glob("snapshots/*/adapter_config.json"), key = _mtime, reverse = True repo_dir.glob("snapshots/*/adapter_config.json"), key = _mtime, reverse = True
) )
for cfg_path in candidates: for cfg_path in candidates:
if cfg_path.is_file(): if cfg_path.is_file():
base = json.loads(cfg_path.read_text()).get("base_model_name_or_path") base = json.loads(cfg_path.read_text(encoding = "utf-8")).get(
"base_model_name_or_path"
)
return base or None return base or None
except Exception as exc: except Exception as exc:
logger.debug("HF cache adapter_config.json lookup failed for '%s': %s", model_name, exc) logger.debug("HF cache adapter_config.json lookup failed for '%s': %s", model_name, exc)
@ -611,7 +616,7 @@ def _check_tokenizer_config_needs_v5(model_name: str, hf_token: str | None = Non
local_tc = local_path / "tokenizer_config.json" local_tc = local_path / "tokenizer_config.json"
if _safe_is_file(local_tc): if _safe_is_file(local_tc):
try: try:
with open(local_tc) as f: with open(local_tc, encoding = "utf-8") as f:
data = json.load(f) data = json.load(f)
tokenizer_class = data.get("tokenizer_class", "") tokenizer_class = data.get("tokenizer_class", "")
result = tokenizer_class in _TRANSFORMERS_5_TOKENIZER_CLASSES result = tokenizer_class in _TRANSFORMERS_5_TOKENIZER_CLASSES
@ -688,7 +693,12 @@ def _config_json_from_hf_cache(model_name: str) -> dict | None:
ref_main = repo_dir / "refs" / "main" ref_main = repo_dir / "refs" / "main"
try: try:
if ref_main.is_file(): if ref_main.is_file():
candidates.append(repo_dir / "snapshots" / ref_main.read_text().strip() / "config.json") candidates.append(
repo_dir
/ "snapshots"
/ ref_main.read_text(encoding = "utf-8").strip()
/ "config.json"
)
# No refs/main (e.g. commit-pinned downloads): newest snapshot by mtime, not a stale # No refs/main (e.g. commit-pinned downloads): newest snapshot by mtime, not a stale
# lexicographically-first SHA, matching what the Hub cache would actually load. # lexicographically-first SHA, matching what the Hub cache would actually load.
candidates += sorted( candidates += sorted(
@ -696,7 +706,7 @@ def _config_json_from_hf_cache(model_name: str) -> dict | None:
) )
for cfg_path in candidates: for cfg_path in candidates:
if cfg_path.is_file(): if cfg_path.is_file():
with open(cfg_path) as f: with open(cfg_path, encoding = "utf-8") as f:
return json.load(f) return json.load(f)
except Exception as exc: except Exception as exc:
logger.debug("HF cache config.json lookup failed for '%s': %s", model_name, exc) logger.debug("HF cache config.json lookup failed for '%s': %s", model_name, exc)
@ -721,7 +731,7 @@ def _load_config_json(model_name: str, hf_token: str | None = None) -> dict | No
local_cfg = Path(model_name) / "config.json" local_cfg = Path(model_name) / "config.json"
if _safe_is_file(local_cfg): if _safe_is_file(local_cfg):
try: try:
with open(local_cfg) as f: with open(local_cfg, encoding = "utf-8") as f:
cfg = json.load(f) cfg = json.load(f)
_config_json_cache[cache_key] = cfg _config_json_cache[cache_key] = cfg
return cfg return cfg
@ -1755,7 +1765,7 @@ def _venv_dir_is_valid(venv_dir: str, packages: tuple[str, ...]) -> bool:
metadata = di / "METADATA" metadata = di / "METADATA"
if not metadata.is_file(): if not metadata.is_file():
continue continue
for line in metadata.read_text(errors = "replace").splitlines(): for line in metadata.read_text(errors = "replace", encoding = "utf-8").splitlines():
if line.startswith("Version:"): if line.startswith("Version:"):
installed_ver = line.split(":", 1)[1].strip() installed_ver = line.split(":", 1)[1].strip()
if installed_ver != pkg_version: if installed_ver != pkg_version:
@ -2391,7 +2401,10 @@ def _llmcompressor_shadow_is_valid() -> bool:
"""True if the shadow dir exists with a marker matching the current pin fingerprint.""" """True if the shadow dir exists with a marker matching the current pin fingerprint."""
marker = Path(_VENV_LLMCOMPRESSOR_DIR) / _LLMC_SHADOW_MARKER marker = Path(_VENV_LLMCOMPRESSOR_DIR) / _LLMC_SHADOW_MARKER
try: try:
return marker.is_file() and marker.read_text().strip() == _LLMC_SHADOW_FINGERPRINT return (
marker.is_file()
and marker.read_text(encoding = "utf-8").strip() == _LLMC_SHADOW_FINGERPRINT
)
except Exception: except Exception:
return False return False
@ -2460,7 +2473,7 @@ def _ensure_venv_llmcompressor_exists() -> bool:
if result.returncode == 0: if result.returncode == 0:
try: try:
(Path(_VENV_LLMCOMPRESSOR_DIR) / _LLMC_SHADOW_MARKER).write_text( (Path(_VENV_LLMCOMPRESSOR_DIR) / _LLMC_SHADOW_MARKER).write_text(
_LLMC_SHADOW_FINGERPRINT _LLMC_SHADOW_FINGERPRINT, encoding = "utf-8"
) )
except Exception: except Exception:
pass pass

View file

@ -108,7 +108,7 @@ def hf_cache_snapshot_dir(model_name: str) -> Optional[Path]:
ref = repo_dir / "refs" / "main" ref = repo_dir / "refs" / "main"
if not ref.is_file(): if not ref.is_file():
continue continue
commit = ref.read_text().strip() commit = ref.read_text(encoding = "utf-8").strip()
if not commit: if not commit:
continue continue
snapshot = repo_dir / "snapshots" / commit snapshot = repo_dir / "snapshots" / commit

View file

@ -2504,7 +2504,7 @@ def detect_host() -> HostInfo:
if is_linux: if is_linux:
for _vendor_file in glob.glob("/sys/class/drm/card*/device/vendor"): for _vendor_file in glob.glob("/sys/class/drm/card*/device/vendor"):
try: try:
with open(_vendor_file) as _vf: with open(_vendor_file, encoding = "utf-8") as _vf:
if _vf.read().strip().lower() == "0x8086": if _vf.read().strip().lower() == "0x8086":
has_intel_gpu = True has_intel_gpu = True
break break
@ -3050,7 +3050,7 @@ def _detect_host_rocm_version() -> tuple[int, int] | None:
os.path.join(rocm_root, "lib", "rocm_version"), os.path.join(rocm_root, "lib", "rocm_version"),
): ):
try: try:
with open(path) as fh: with open(path, encoding = "utf-8") as fh:
parts = fh.read().strip().split("-")[0].split(".") parts = fh.read().strip().split("-")[0].split(".")
# Explicit length guard avoids relying on the broad except # Explicit length guard avoids relying on the broad except
# below to swallow IndexError when the version file contains # below to swallow IndexError when the version file contains
@ -5526,7 +5526,9 @@ def write_prebuilt_metadata(
"prebuilt_fallback_used": prebuilt_fallback_used, "prebuilt_fallback_used": prebuilt_fallback_used,
"installed_at_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "installed_at_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
} }
(install_dir / "UNSLOTH_PREBUILT_INFO.json").write_text(json.dumps(metadata, indent = 2) + "\n") (install_dir / "UNSLOTH_PREBUILT_INFO.json").write_text(
json.dumps(metadata, indent = 2) + "\n", encoding = "utf-8"
)
def sync_marker_force_cpu(install_dir: Path, persist_force_cpu: bool) -> None: def sync_marker_force_cpu(install_dir: Path, persist_force_cpu: bool) -> None:
@ -5537,13 +5539,13 @@ def sync_marker_force_cpu(install_dir: Path, persist_force_cpu: bool) -> None:
GPU/Vulkan bundle that revives the crash (#7213).""" GPU/Vulkan bundle that revives the crash (#7213)."""
marker_path = install_dir / "UNSLOTH_PREBUILT_INFO.json" marker_path = install_dir / "UNSLOTH_PREBUILT_INFO.json"
try: try:
marker = json.loads(marker_path.read_text()) marker = json.loads(marker_path.read_text(encoding = "utf-8"))
except (OSError, ValueError): except (OSError, ValueError):
return return
if not isinstance(marker, dict) or bool(marker.get("force_cpu")) == persist_force_cpu: if not isinstance(marker, dict) or bool(marker.get("force_cpu")) == persist_force_cpu:
return return
marker["force_cpu"] = persist_force_cpu marker["force_cpu"] = persist_force_cpu
marker_path.write_text(json.dumps(marker, indent = 2) + "\n") marker_path.write_text(json.dumps(marker, indent = 2) + "\n", encoding = "utf-8")
log(f"existing install reused; recorded force_cpu={persist_force_cpu} from this run") log(f"existing install reused; recorded force_cpu={persist_force_cpu} from this run")

View file

@ -535,7 +535,7 @@ def install_lock(lock_path: Path) -> Iterator[None]:
break break
except FileExistsError: except FileExistsError:
try: try:
raw = lock_path.read_text().strip() raw = lock_path.read_text(encoding = "utf-8").strip()
except FileNotFoundError: except FileNotFoundError:
continue continue
stale = False stale = False
@ -660,7 +660,7 @@ def write_metadata(install_dir: Path, *, version: str, asset: str, sha256: str)
"asset": asset, "asset": asset,
"sha256": sha256, "sha256": sha256,
} }
metadata_path(install_dir).write_text(json.dumps(payload, indent = 2) + "\n") metadata_path(install_dir).write_text(json.dumps(payload, indent = 2) + "\n", encoding = "utf-8")
def load_metadata(install_dir: Path) -> dict | None: def load_metadata(install_dir: Path) -> dict | None:
@ -668,7 +668,7 @@ def load_metadata(install_dir: Path) -> dict | None:
if not path.exists(): if not path.exists():
return None return None
try: try:
data = json.loads(path.read_text()) data = json.loads(path.read_text(encoding = "utf-8"))
except (json.JSONDecodeError, OSError): except (json.JSONDecodeError, OSError):
return None return None
return data if isinstance(data, dict) else None return data if isinstance(data, dict) else None

View file

@ -503,7 +503,7 @@ def _detect_rocm_version() -> tuple[int, int] | None:
os.path.join(rocm_root, "lib", "rocm_version"), os.path.join(rocm_root, "lib", "rocm_version"),
): ):
try: try:
with open(path) as fh: with open(path, encoding = "utf-8") as fh:
parts = fh.read().strip().split("-")[0].split(".") parts = fh.read().strip().split("-")[0].split(".")
# Explicit length guard: don't rely on the broad except below to # Explicit length guard: don't rely on the broad except below to
# swallow IndexError on a single-component version (e.g. "6\n"). # swallow IndexError on a single-component version (e.g. "6\n").
@ -852,9 +852,9 @@ def _linux_amd_display_device_present() -> bool:
try: try:
for dev in Path("/sys/bus/pci/devices").iterdir(): for dev in Path("/sys/bus/pci/devices").iterdir():
try: try:
if (dev / "vendor").read_text().strip() != "0x1002": if (dev / "vendor").read_text(encoding = "utf-8").strip() != "0x1002":
continue continue
if (dev / "class").read_text().strip().startswith("0x03"): if (dev / "class").read_text(encoding = "utf-8").strip().startswith("0x03"):
return True return True
except OSError: except OSError:
continue continue
@ -1067,7 +1067,7 @@ def _has_rocm_gpu() -> bool:
for entry in os.listdir(kfd_nodes): for entry in os.listdir(kfd_nodes):
gpu_id_path = os.path.join(kfd_nodes, entry, "gpu_id") gpu_id_path = os.path.join(kfd_nodes, entry, "gpu_id")
try: try:
with open(gpu_id_path) as fh: with open(gpu_id_path, encoding = "utf-8") as fh:
gpu_id = fh.read().strip() gpu_id = fh.read().strip()
except OSError: except OSError:
continue continue
@ -1079,7 +1079,7 @@ def _has_rocm_gpu() -> bool:
# false positive (e.g. NVIDIA open-driver KFD nodes lacking it). # false positive (e.g. NVIDIA open-driver KFD nodes lacking it).
props_path = os.path.join(kfd_nodes, entry, "properties") props_path = os.path.join(kfd_nodes, entry, "properties")
try: try:
with open(props_path) as fh: with open(props_path, encoding = "utf-8") as fh:
props = fh.read() props = fh.read()
except OSError: except OSError:
continue # can't confirm vendor -- skip continue # can't confirm vendor -- skip

View file

@ -1078,7 +1078,7 @@ def install_lock(lock_path: Path) -> Iterator[None]:
except FileExistsError: except FileExistsError:
stale = False stale = False
try: try:
raw = lock_path.read_text().strip() raw = lock_path.read_text(encoding = "utf-8").strip()
except FileNotFoundError: except FileNotFoundError:
# Lock vanished between our open and read -- retry # Lock vanished between our open and read -- retry
continue continue

View file

@ -0,0 +1,407 @@
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
"""Guard: shipping code must name an encoding on every text read and write.
`Path.read_text()`, `Path.write_text()`, `Path.open()` and builtin `open()` fall back
to `locale.getencoding()`: UTF-8 on the Linux and macOS runners, cp1252 on a stock
Windows install. Every file this repo reads at runtime is UTF-8 (HF `config.json` /
`tokenizer_config.json` / `adapter_config.json`, Ollama manifests, GGUF export
metadata), so on Windows those reads crash or, worse, succeed with mojibake: a
DeepSeek or Qwen tokenizer_config.json carries U+FF5C and U+2581 in its chat
template, and at utils/models/model_config.py that read sits inside a broad
`except Exception: logger.debug(...)`, so the token-pattern check silently
returned the wrong answer.
Unlike the import-time rule in test_source_read_encoding.py this is scope agnostic:
runtime reads live inside functions, and shipping code has no legitimate reason to
let the operator's locale decide. No reachability analysis to get wrong, so no
allowlist and no false positives.
Binary handles are skipped (no encoding to name, and passing one is a ValueError),
and a non-constant mode counts as unknown rather than text: demanding `encoding =`
on a call that may resolve to "rb" would leave no compliant way to write it.
Known limitation, deliberately not closed: `configparser.ConfigParser.read()` also
defaults to the locale encoding, but cannot be matched by name without resolving the
receiver, since `f.read(n)`, `resp.read(limit)` and `handle.read(chunk)` are spelled
identically. Flagging it would be a false positive with no compliant fix, the exact
failure mode this guard avoids. The one live `ConfigParser.read` (/etc/wsl.conf,
hub/utils/paths.py) is pinned by hand; a future one has to be caught in review.
"""
# `str | None` below is evaluated at import on Python 3.9 (requires-python >= 3.9).
from __future__ import annotations
import ast
import subprocess
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
# Everything that ships. `studio/` covers the installers too: install_python_stack.py
# reads /sys/class/kfd, the same detection path as utils/hardware/hardware.py. Test
# trees fall under the narrower import-time rule in test_source_read_encoding.py.
ROOTS = (REPO / "unsloth", REPO / "studio", REPO / "unsloth_cli")
# The frontend tree is TypeScript; node_modules is vendored third-party code.
SKIP_DIRS = {"build", "dist", "frontend", "node_modules", "src-tauri", ".venv", "site-packages"}
GUARDED_METHODS = {"read_text", "write_text"}
# Path classes, so an unbound `Path.open(p)` shifts every argument one right.
PATH_CLASSES = {"Path", "PosixPath", "PurePath", "WindowsPath"}
# Values that re-select the platform default when passed as the encoding.
PLATFORM_DEFAULT_ENCODINGS = (None, "locale")
# Calls that return the platform default, so naming one pins nothing.
PLATFORM_DEFAULT_CALLS = {"getdefaultencoding", "getencoding", "getpreferredencoding"}
# Modules whose `open` IS the builtin: same signature, same platform default.
BUILTIN_OPEN_MODULES = {"builtins", "io"}
# Take an encoding in "t" mode but default to "rb". Value is its positional slot.
COMPRESSED_OPENERS = {"bz2": 3, "gzip": 3, "lzma": None}
# Distinct from None so that "no mode argument at all" still means text.
UNKNOWN_MODE = object()
def _mode(call: ast.Call, positional_index: int):
"""The call's mode, or UNKNOWN_MODE when it is not a literal."""
# A splat hides the mode, so it is unknown rather than absent: falling through to
# "r" would flag a call that may resolve to binary, with no compliant way to fix it.
if any(isinstance(a, ast.Starred) for a in call.args):
return UNKNOWN_MODE
if any(kw.arg is None for kw in call.keywords):
return UNKNOWN_MODE
if len(call.args) > positional_index:
node = call.args[positional_index]
return node.value if isinstance(node, ast.Constant) else UNKNOWN_MODE
for kw in call.keywords:
if kw.arg == "mode":
return kw.value.value if isinstance(kw.value, ast.Constant) else UNKNOWN_MODE
return "r"
def _names_encoding(call: ast.Call) -> bool:
"""True only for an encoding that actually pins one.
`encoding = None` and `encoding = "locale"` re-select the platform default, so the
keyword being present is not enough. A `**kwargs` splat may carry an encoding we
cannot see, so it counts as named rather than as an unsatisfiable demand.
"""
for kw in call.keywords:
if kw.arg is None:
return True
if kw.arg != "encoding":
continue
if isinstance(kw.value, ast.Constant) and kw.value.value in PLATFORM_DEFAULT_ENCODINGS:
return False
if isinstance(kw.value, ast.Call) and _callee_name(kw.value.func) in PLATFORM_DEFAULT_CALLS:
return False # locale.getencoding() is the default, spelled out
return True
return False
def _is_text(call: ast.Call, positional_index: int) -> bool:
mode = _mode(call, positional_index)
return mode is not UNKNOWN_MODE and "b" not in str(mode)
def _imports_at_each_call(tree: ast.Module) -> dict:
"""The imports visible at every call, keyed by node id.
A function's own imports stay in that function: hoisting them would let one local
`from PIL.Image import open` turn off the builtin check for the whole file.
"""
visible_at = {}
def walk(node, visible):
if isinstance(node, ast.Call):
visible_at[id(node)] = visible
for child in ast.iter_child_nodes(node):
if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)):
walk(child, {**visible, **_imported_names(child)})
else:
walk(child, visible)
walk(tree, _imported_names(tree))
return visible_at
def _foreign_names(tree: ast.Module) -> set:
"""Names bound to an object another library built.
`z = zipfile.ZipFile(p)` then `z.open(name)` is a binary member stream taking no
encoding, so demanding one leaves no correct edit.
"""
modules = _imported_names(tree)
names = set()
for node in ast.walk(tree):
if not isinstance(node, ast.Assign) or not isinstance(node.value, ast.Call):
continue
if _foreign_receiver(node.value, modules):
names.update(t.id for t in node.targets if isinstance(t, ast.Name))
return names
def _imported_names(tree) -> dict:
"""Names this module's imports bind, mapped to where they came from.
The name alone settles nothing: `import tarfile as tf` hides an opener that takes
no encoding, and `from PIL.Image import open` puts another behind the most familiar
name there is. Resolving the origin covers both, with no module list to maintain.
"""
bound = {}
stack = list(ast.iter_child_nodes(tree))
while stack:
node = stack.pop()
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)):
continue # that function's business, not this scope's
if isinstance(node, ast.Import):
for a in node.names:
bound[(a.asname or a.name).split(".")[0]] = a.name
elif isinstance(node, ast.ImportFrom):
for a in node.names:
bound[a.asname or a.name] = f"{node.module}.{a.name}" if node.module else a.name
else:
stack.extend(ast.iter_child_nodes(node))
return bound
def _callee_name(func):
"""The bare name a callee ends in, whether or not it is qualified."""
return func.id if isinstance(func, ast.Name) else getattr(func, "attr", None)
def _origin_root(name, modules) -> str:
"""The top-level module a bound name came from, or the name itself."""
return modules.get(name, name).split(".")[0]
def _compressed_key(name, modules):
"""The COMPRESSED_OPENERS entry this receiver resolves to, if any."""
for candidate in (name, _origin_root(name, modules)):
if candidate in COMPRESSED_OPENERS:
return candidate
return None
def _open_alias(name, modules):
"""What a bare callable resolves to: "builtin", a COMPRESSED_OPENERS key, or None."""
origin = modules.get(name)
if origin is None:
return "builtin" if name == "open" else None
parts = origin.split(".")
if parts[-1] != "open":
return None
if parts[0] in BUILTIN_OPEN_MODULES or origin == "open":
return "builtin"
return parts[0] if parts[0] in COMPRESSED_OPENERS else None
def _is_path_class(name, modules) -> bool:
"""True for a pathlib class, including under an alias."""
if name is None:
return False
return (modules.get(name) or name).split(".")[-1] in PATH_CLASSES
def _is_path_attr(node) -> bool:
"""True for a qualified path class, as in `pathlib.Path`."""
return isinstance(node, ast.Attribute) and node.attr in PATH_CLASSES
def _foreign_receiver(node, modules) -> bool:
"""True when the thing before `.open` is an object another library built.
`zipfile.ZipFile(p).open(name)` returns a binary member stream taking no encoding,
so it needs the same exemption as the bare `zipfile.open` spelling.
"""
if not isinstance(node, ast.Call):
return False
func = node.func
if isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name):
root, name = func.value.id, func.attr
elif isinstance(func, ast.Name):
root = name = func.id
else:
return False
return root in modules and not _is_path_class(name, modules)
def _offender(
call: ast.Call,
modules = None,
foreign = (),
) -> str | None:
"""The call's name if it does text I/O without pinning an encoding."""
modules = {} if modules is None else modules
func = call.func
if isinstance(func, ast.Attribute):
receiver = func.value.id if isinstance(func.value, ast.Name) else None
# `Path.read_text(p)` is `p.read_text()` unbound: the instance takes slot 0,
# so every argument shifts one place right.
shift = 1 if _is_path_class(receiver, modules) or _is_path_attr(func.value) else 0
if func.attr in GUARDED_METHODS:
if func.attr == "read_text" and not shift and call.args:
first = call.args[0]
# Bound read_text takes encoding first, so None or "locale" there is a
# platform-default read. Any other positional means the receiver is
# importlib.metadata's Distribution: a filename, and no encoding at all.
if isinstance(first, ast.Constant) and first.value in PLATFORM_DEFAULT_ENCODINGS:
return "read_text()"
return None
return None if _names_encoding(call) else f"{func.attr}()"
if func.attr == "open":
if receiver is not None and _origin_root(receiver, modules) in BUILTIN_OPEN_MODULES:
return (
None if not _is_text(call, 1) or _names_encoding(call) else f"{receiver}.open()"
)
compressed = _compressed_key(receiver, modules) if receiver else None
if compressed is not None:
# "rb" by default, so only an explicit text mode is in scope.
mode = _mode(call, 1)
if mode is UNKNOWN_MODE or "t" not in str(mode):
return None
return None if _names_encoding(call) else f"{compressed}.open()"
# Any other imported receiver is somebody else's opener: tarfile takes a
# compression mode, Image a binary file. Neither has an encoding to name.
if receiver is not None and receiver in modules and receiver not in PATH_CLASSES:
return None
if _foreign_receiver(func.value, modules) or receiver in foreign:
return None
if not _is_text(call, shift):
return None
return None if _names_encoding(call) else "Path.open()"
return None
if isinstance(func, ast.Name):
alias = _open_alias(func.id, modules)
if alias == "builtin":
if not _is_text(call, 1):
return None
return None if _names_encoding(call) else "open()"
if alias is not None:
mode = _mode(call, 1)
if mode is UNKNOWN_MODE or "t" not in str(mode):
return None
return None if _names_encoding(call) else f"{alias}.open()"
return None
def _is_test_path(path: Path) -> bool:
parts = path.relative_to(REPO).parts
if SKIP_DIRS.intersection(parts):
return True
if "tests" in parts or "test" in parts:
return True
return path.name.startswith("test_") or path.name.endswith("_test.py")
def _offenders_in(src: str, label: str = "<snippet>"):
tree = ast.parse(src, filename = label)
visible_at = _imports_at_each_call(tree)
foreign = _foreign_names(tree)
found = []
for node in ast.walk(tree):
if isinstance(node, ast.Call):
name = _offender(node, visible_at.get(id(node), {}), foreign)
if name is not None:
found.append((node.lineno, name))
return found
def _tracked_sources():
"""Shipping *.py that git is actually tracking.
A walk also picks up whatever is lying in the checkout (a built `build/lib` copy,
a nested worktree, a vendored dep). None of those are ours to police, and a stale
artifact would fail this for everybody who has one.
"""
listed = subprocess.run(
["git", "-C", str(REPO), "ls-files", "-z", "--", "*.py"],
capture_output = True,
timeout = 60,
)
if listed.returncode != 0:
return None # not a checkout, so fall back to walking
names = listed.stdout.decode("utf-8", errors = "replace").split("\0")
return [REPO / n for n in names if n]
def _walked_sources():
return [p for root in ROOTS if root.is_dir() for p in sorted(root.rglob("*.py"))]
def test_shipping_code_names_an_encoding():
offenders = []
sources = _tracked_sources()
if sources is None:
sources = _walked_sources()
roots = {r.resolve() for r in ROOTS}
for path in sorted(sources):
if not roots.intersection(path.resolve().parents) or _is_test_path(path):
continue
try:
tree = ast.parse(path.read_text(encoding = "utf-8"), filename = str(path))
except SyntaxError:
continue
rel = path.relative_to(REPO).as_posix()
visible_at = _imports_at_each_call(tree)
foreign = _foreign_names(tree)
for node in ast.walk(tree):
if isinstance(node, ast.Call):
name = _offender(node, visible_at.get(id(node), {}), foreign)
if name is not None:
offenders.append(f"{rel}:{node.lineno}: {name}")
assert offenders == [], (
f"{len(offenders)} text read/write call sites in shipping code let the "
"operator's locale decide the encoding, so they crash or silently "
'produce mojibake on Windows. Pass encoding = "utf-8": ' + repr(offenders)
)
# The assertion above passes vacuously once the trees are clean, so it cannot tell a
# working detector from one that always returns None. These pin the detector itself.
def test_detects_the_plain_cases():
assert _offenders_in("from pathlib import Path\np = Path('x')\ns = p.read_text()\n")
assert _offenders_in("p.write_text('hi')\n")
assert _offenders_in("f = open('x')\n")
assert _offenders_in("f = open('x', 'w')\n")
assert _offenders_in("f = p.open()\n")
# Inside a function body too: shipping reads are not import-time.
assert _offenders_in("def load(p):\n return p.read_text()\n")
def test_rejects_encoding_that_reselects_the_platform_default():
assert _offenders_in("s = p.read_text(encoding = None)\n")
assert _offenders_in("s = p.read_text(encoding = 'locale')\n")
def test_accepts_a_pinned_encoding():
assert not _offenders_in("s = p.read_text(encoding = 'utf-8')\n")
assert not _offenders_in("f = open('x', 'w', encoding = 'utf-8')\n")
assert not _offenders_in("f = p.open(encoding = 'utf-8')\n")
assert not _offenders_in("s = p.read_text(encoding = 'utf-8', errors = 'replace')\n")
def test_skips_binary_handles():
# Binary has no encoding to name; passing one is a ValueError.
assert not _offenders_in("f = open('x', 'rb')\n")
assert not _offenders_in("f = open('x', mode = 'wb')\n")
assert not _offenders_in("f = p.open('rb')\n")
def test_skips_unknown_modes():
# A call that may resolve to "rb" has no compliant way to name an encoding.
assert not _offenders_in("mode = 'rb' if binary else 'r'\nf = open(path, mode)\n")
assert not _offenders_in("f = open(path, mode = chosen)\n")
def test_skips_foreign_openers_and_readers():
assert not _offenders_in("import fitz\nd = fitz.open(stream = b, filetype = 'pdf')\n")
assert not _offenders_in("import tarfile\nt = tarfile.open(p, 'r:gz')\n")
# importlib.metadata Distribution.read_text takes a positional filename.
assert not _offenders_in("s = dist.read_text('direct_url.json')\n")
def test_test_trees_are_out_of_scope():
assert _is_test_path(REPO / "tests" / "test_x.py")
assert _is_test_path(REPO / "studio" / "backend" / "tests" / "helpers.py")
assert not _is_test_path(REPO / "studio" / "backend" / "routes" / "inference.py")

View file

@ -2471,7 +2471,7 @@ def _get_statistics(statistics = None, force_download = True):
for vendor_file in vendor_files: for vendor_file in vendor_files:
path = Path(vendor_file) path = Path(vendor_file)
if path.is_file(): if path.is_file():
file_content = path.read_text().lower() file_content = path.read_text(encoding = "utf-8").lower()
if "amazon" in file_content: if "amazon" in file_content:
return "aws" return "aws"
elif "microsoft corporation" in file_content: elif "microsoft corporation" in file_content:

View file

@ -1585,7 +1585,7 @@ class FastModel(FastBaseModel):
if do_logging: if do_logging:
redirector = contextlib.nullcontext() redirector = contextlib.nullcontext()
else: else:
redirector = contextlib.redirect_stdout(open(os.devnull, "w")) redirector = contextlib.redirect_stdout(open(os.devnull, "w", encoding = "utf-8"))
model_types = ["siglip"] + model_types model_types = ["siglip"] + model_types
# Set forced float32 env flag # Set forced float32 env flag

View file

@ -450,7 +450,7 @@ def _load_fp8_weight_map(
index_path = None index_path = None
if index_path is not None: if index_path is not None:
import json import json
with open(index_path, "r") as f: with open(index_path, "r", encoding = "utf-8") as f:
return json.load(f).get("weight_map", None) return json.load(f).get("weight_map", None)
# Unsharded single file: map every tensor to it. # Unsharded single file: map every tensor to it.

View file

@ -2329,7 +2329,7 @@ def _patch_st_trainer_load_from_checkpoint():
if not os.path.isfile(modules_json): if not os.path.isfile(modules_json):
raise RuntimeError("Unsloth: PEFT checkpoint is missing modules.json.") raise RuntimeError("Unsloth: PEFT checkpoint is missing modules.json.")
try: try:
with open(modules_json, "r") as f: with open(modules_json, "r", encoding = "utf-8") as f:
module_configs = json.load(f) module_configs = json.load(f)
except Exception as e: except Exception as e:
raise RuntimeError("Unsloth: Cannot parse checkpoint modules.json.") from e raise RuntimeError("Unsloth: Cannot parse checkpoint modules.json.") from e

View file

@ -98,7 +98,7 @@ def _json_rank_count_from_env(name: str) -> Optional[int]:
if value.lstrip().startswith(("[", "{")): if value.lstrip().startswith(("[", "{")):
data = json.loads(value) data = json.loads(value)
else: else:
with open(value, "r") as f: with open(value, "r", encoding = "utf-8") as f:
data = json.load(f) data = json.load(f)
except (OSError, json.JSONDecodeError): except (OSError, json.JSONDecodeError):
return None return None
@ -158,7 +158,7 @@ def quiet_if_nonzero_mlx_rank():
sys.stderr.flush() sys.stderr.flush()
saved_stdout_fd = os.dup(1) saved_stdout_fd = os.dup(1)
saved_stderr_fd = os.dup(2) saved_stderr_fd = os.dup(2)
with open(os.devnull, "w") as devnull: with open(os.devnull, "w", encoding = "utf-8") as devnull:
try: try:
os.dup2(devnull.fileno(), 1) os.dup2(devnull.fileno(), 1)
os.dup2(devnull.fileno(), 2) os.dup2(devnull.fileno(), 2)

View file

@ -719,7 +719,7 @@ def _cli_update_password(conn: sqlite3.Connection, username: str, new_password:
# credential after a later reset-password deletes auth.db. Mirrors # credential after a later reset-password deletes auth.db. Mirrors
# backend clear_bootstrap_password(). # backend clear_bootstrap_password().
try: try:
stale_path.write_text("") stale_path.write_text("", encoding = "utf-8")
cleared = True cleared = True
except OSError: except OSError:
cleared = False cleared = False
@ -2406,7 +2406,7 @@ def stop():
typer.echo("No running Unsloth server found (no PID file).") typer.echo("No running Unsloth server found (no PID file).")
raise typer.Exit(0) raise typer.Exit(0)
pid_text = _PID_FILE.read_text().strip() pid_text = _PID_FILE.read_text(encoding = "utf-8").strip()
if not pid_text.isdigit(): if not pid_text.isdigit():
typer.echo(f"Invalid PID file contents: {pid_text}") typer.echo(f"Invalid PID file contents: {pid_text}")
_PID_FILE.unlink(missing_ok = True) _PID_FILE.unlink(missing_ok = True)
@ -2863,7 +2863,7 @@ def reset_password():
path.unlink(missing_ok = True) path.unlink(missing_ok = True)
except OSError: except OSError:
try: try:
path.write_text("") path.write_text("", encoding = "utf-8")
except OSError as exc: except OSError as exc:
typer.echo( typer.echo(
f"Error: could not remove or clear {path.name} ({exc}); delete " f"Error: could not remove or clear {path.name} ({exc}); delete "