From 9c3ea5d53121cabffe8fb9ea2c7c45bd5f08e8c7 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:17:17 -0500 Subject: [PATCH 1/3] fix: ensure that pin metadata files are closed after reading them in --- pins/boards.py | 54 +++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/pins/boards.py b/pins/boards.py index c1eeb2f2..f74e6008 100644 --- a/pins/boards.py +++ b/pins/boards.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import functools import inspect import logging @@ -156,9 +157,8 @@ def pin_meta(self, name, version: str = None) -> Meta: meta_name = self.meta_factory.get_meta_name(*components) path_meta = self.construct_path([*components, meta_name]) - f, local = self._open_pin_meta(path_meta) - - meta = self.meta_factory.read_pin_yaml(f, pin_name, selected_version, local=local) + with self._open_pin_meta(path_meta) as (f, local): + meta = self.meta_factory.read_pin_yaml(f, pin_name, selected_version, local=local) return meta @@ -788,14 +788,18 @@ def _load_data(self, meta, pin_version_path): # filesystem and cache methods -------------------------------------------- + @contextlib.contextmanager def _open_pin_meta(self, path): f = self.fs.open(path) - self._touch_cache(path) + try: + self._touch_cache(path) - # optional additional data to put in Meta.local - local = {} + # optional additional data to put in Meta.local + local = {} - return f, local + yield f, local + finally: + self.fs.close(f) def _get_cache_path(self, pin_name, version=None, fname=None): version_part = [version] if version is not None else [] @@ -934,8 +938,8 @@ def pin_meta(self, name, version=None): # note that pins on this board should point to versions, so we use an # empty string to mark version (it ultimately is ignored) path_meta = self.construct_path([pin_name, "", meta_name]) - f, local = self._open_pin_meta(path_meta) - meta = self.meta_factory.read_pin_yaml(f, pin_name, VersionRaw(""), local=local) + with self._open_pin_meta(path_meta) as (f, local): + meta = self.meta_factory.read_pin_yaml(f, pin_name, VersionRaw(""), local=local) # TODO(#59,#83): handle caching, and then re-enable pin_read. # self._touch_cache(path_meta) @@ -1143,22 +1147,26 @@ def pin_versions_prune(self, *args, **kwargs): ) super().pin_versions_prune(*args, **kwargs) + @contextlib.contextmanager def _open_pin_meta(self, path): f = self.fs.open(path) - self._touch_cache(path) - - # optional additional data to put in Meta.local - user_name, content_name, bundle_id = str(path).split("/")[:3] - user_guid = self.fs._user_name_cache[user_name] - content_guid = self.fs._content_name_cache[(user_guid, content_name)] - - local = { - "content_id": content_guid, - "version": bundle_id, - "url": f"{self.fs.api.server_url}/content/{content_guid}/", - } - - return f, local + try: + self._touch_cache(path) + + # optional additional data to put in Meta.local + user_name, content_name, bundle_id = str(path).split("/")[:3] + user_guid = self.fs._user_name_cache[user_name] + content_guid = self.fs._content_name_cache[(user_guid, content_name)] + + local = { + "content_id": content_guid, + "version": bundle_id, + "url": f"{self.fs.api.server_url}/content/{content_guid}/", + } + + yield f, local + finally: + self.fs.close(f) def validate_pin_name(self, name) -> None: # this should be the default behavior, expecting a full pin name. From cdff26f774a28aa19e7c984801c88f6c283e6631 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:19:03 -0500 Subject: [PATCH 2/3] chore: take advantage of existing context managers --- pins/boards.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pins/boards.py b/pins/boards.py index f74e6008..edc9331e 100644 --- a/pins/boards.py +++ b/pins/boards.py @@ -790,16 +790,13 @@ def _load_data(self, meta, pin_version_path): @contextlib.contextmanager def _open_pin_meta(self, path): - f = self.fs.open(path) - try: + with self.fs.open(path) as f: self._touch_cache(path) # optional additional data to put in Meta.local local = {} yield f, local - finally: - self.fs.close(f) def _get_cache_path(self, pin_name, version=None, fname=None): version_part = [version] if version is not None else [] @@ -1149,8 +1146,7 @@ def pin_versions_prune(self, *args, **kwargs): @contextlib.contextmanager def _open_pin_meta(self, path): - f = self.fs.open(path) - try: + with self.fs.open(path) as f: self._touch_cache(path) # optional additional data to put in Meta.local @@ -1165,8 +1161,6 @@ def _open_pin_meta(self, path): } yield f, local - finally: - self.fs.close(f) def validate_pin_name(self, name) -> None: # this should be the default behavior, expecting a full pin name. From 61a97b1da957a7f4ef594d0337fdc750d1ebe34c Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:25:12 -0500 Subject: [PATCH 3/3] style: fix lints --- pins/boards.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pins/boards.py b/pins/boards.py index edc9331e..893be522 100644 --- a/pins/boards.py +++ b/pins/boards.py @@ -158,7 +158,9 @@ def pin_meta(self, name, version: str = None) -> Meta: path_meta = self.construct_path([*components, meta_name]) with self._open_pin_meta(path_meta) as (f, local): - meta = self.meta_factory.read_pin_yaml(f, pin_name, selected_version, local=local) + meta = self.meta_factory.read_pin_yaml( + f, pin_name, selected_version, local=local + ) return meta @@ -936,7 +938,9 @@ def pin_meta(self, name, version=None): # empty string to mark version (it ultimately is ignored) path_meta = self.construct_path([pin_name, "", meta_name]) with self._open_pin_meta(path_meta) as (f, local): - meta = self.meta_factory.read_pin_yaml(f, pin_name, VersionRaw(""), local=local) + meta = self.meta_factory.read_pin_yaml( + f, pin_name, VersionRaw(""), local=local + ) # TODO(#59,#83): handle caching, and then re-enable pin_read. # self._touch_cache(path_meta)