Skip to content

Commit 732c800

Browse files
committed
Address review: deterministic dir-name tests + doc callouts
- test_full_session_id_remains_in_the_log: filter to openjd.sessions records and default the session_id getattr to None, so a dropped LoggerAdapter fails the assertion instead of passing vacuously. - Replace the probabilistic name-inspection assertions with a deterministic check that TempDir is called with prefix="". - Assert the name length as a <= budget ceiling rather than == to an incidental CPython detail. - Narrow the concurrent test's except from BaseException to Exception (CodeQL). - Fix TempDir.prefix docstring (default is None -> "tmp", not ""). - Note on working_directory / retain_working_dir that the directory name no longer carries session identity; the log holds that mapping. Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
1 parent b6e4f8d commit 732c800

3 files changed

Lines changed: 47 additions & 21 deletions

File tree

src/openjd/sessions/_session.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,12 @@ def __init__(
417417
within all actions running within this session. Defaults to None.
418418
retain_working_dir (bool, optional): If set, then the Session's Working Directory
419419
is not deleted when this Session object is deleted. Defaults to False.
420+
Note: the working directory's *name* no longer encodes the session id
421+
(it is mkdtemp()'s random characters and nothing else), so a retained
422+
directory is not attributable to a session by its name alone. The
423+
session log records the working directory's full path against the
424+
session id; that log is how a retained or orphaned directory is
425+
resolved back to its session.
420426
user (Optional[SessionUser]): The specific OS user to run subprocesses as, and whom
421427
will have permissions to the Session's Working Directory.
422428
Defaults to the current process user.
@@ -632,6 +638,10 @@ def working_directory(self) -> Path:
632638
This is available in a Job Template's format string expressions as
633639
Session.WorkingDirectory
634640
641+
The directory's name is generated (mkdtemp()'s random characters) and
642+
carries no part of the session id, so do not parse identity out of it;
643+
the session id is recorded against this path in the session log instead.
644+
635645
Raises:
636646
RuntimeError: If this Session has no working directory, which means
637647
construction did not complete.

src/openjd/sessions/_tempdir.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,9 @@ def __init__(
261261
dir (Optional[Path]): The directory in which to create the temp dir.
262262
Defaults to tempfile.gettempdir().
263263
prefix (Optional[str]): A prefix to use in the name of the generated temp dir.
264-
Defaults to "".
264+
Defaults to None, which mkdtemp() replaces with its "tmp" template
265+
(3 characters). Pass "" for no prefix at all -- a shorter name, and
266+
not the same as omitting the argument.
265267
user (Optional[SessionUser]): A group that will own the created directory.
266268
The group-write bit will be set on the directory if this option is supplied.
267269
Defaults to this process' effective user/group.

test/openjd/sessions_v0/test_session.py

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
LogContent,
5555
)
5656
from openjd.sessions import _path_mapping as path_mapping_impl_mod
57+
from openjd.sessions import _session as session_module
5758
from openjd.sessions._action_filter import ActionMessageKind
5859
from openjd.sessions._os_checker import is_posix, is_windows
5960
from openjd.sessions._logging import LoggerAdapter, LogExtraInfo
@@ -564,6 +565,12 @@ class TestSessionWorkingDirectoryName:
564565
DEADLINE_SHAPED_ID = f"session-{uuid.uuid4().hex}"
565566

566567
def test_working_directory_name_length_is_bounded(self) -> None:
568+
# A ceiling, not an equality: the exact count is mkdtemp()'s to choose
569+
# (CPython currently yields 8 characters, hence the constant) and a stdlib
570+
# change to a *shorter* name would not be a regression. What must not
571+
# regress is the budget -- passing the 40-character session id as a prefix
572+
# cost 48.
573+
567574
# GIVEN
568575
job_params = dict[str, ParameterValue]()
569576

@@ -572,28 +579,29 @@ def test_working_directory_name_length_is_bounded(self) -> None:
572579
session_id=self.DEADLINE_SHAPED_ID, job_parameter_values=job_params
573580
) as session:
574581
# THEN
575-
assert len(session.working_directory.name) == SESSION_DIR_NAME_LENGTH
576-
# Guard on the budget itself, independent of the constant: this cost 48.
577-
assert len(session.working_directory.name) <= 8
582+
assert len(session.working_directory.name) <= SESSION_DIR_NAME_LENGTH
578583

579-
def test_working_directory_name_carries_no_part_of_the_session_id(self) -> None:
580-
# prefix="" rather than None: None would restore mkdtemp()'s "tmp" template,
581-
# so guard the distinction and not just the absence of the id.
584+
def test_working_directory_is_created_with_an_empty_prefix(self) -> None:
585+
# prefix="" rather than None is the load-bearing choice: None restores
586+
# mkdtemp()'s "tmp" template (3 characters) and the session id (48) is what
587+
# we removed. Assert the argument we pass, not a sample of the random name:
588+
# inspecting the name for the *absence* of a prefix is probabilistic -- a
589+
# random [a-z0-9_] name can happen to begin "tmp" -- while the call is exact.
582590

583591
# GIVEN
584592
job_params = dict[str, ParameterValue]()
585593

586594
# WHEN
587-
with Session(
588-
session_id=self.DEADLINE_SHAPED_ID, job_parameter_values=job_params
589-
) as session:
590-
# THEN
591-
name = session.working_directory.name
592-
assert not name.startswith("tmp")
593-
assert "session" not in name
594-
assert not any(
595-
self.DEADLINE_SHAPED_ID[-n:] in name for n in range(4, len(self.DEADLINE_SHAPED_ID))
596-
)
595+
with patch.object(session_module, "TempDir", wraps=session_module.TempDir) as mock_tempdir:
596+
with Session(
597+
session_id=self.DEADLINE_SHAPED_ID, job_parameter_values=job_params
598+
) as session:
599+
# THEN
600+
# The first TempDir call is the working directory; the second is
601+
# the nested embedded_files directory.
602+
assert mock_tempdir.call_args_list[0].kwargs["prefix"] == ""
603+
# ... and nothing of the session id survived into the name.
604+
assert self.DEADLINE_SHAPED_ID not in session.working_directory.name
597605

598606
@pytest.mark.usefixtures("caplog") # built-in fixture
599607
def test_full_session_id_remains_in_the_log(self, caplog: pytest.LogCaptureFixture) -> None:
@@ -617,9 +625,15 @@ def test_full_session_id_remains_in_the_log(self, caplog: pytest.LogCaptureFixtu
617625
m == f"Session Working Directory: {str(session.working_directory)}"
618626
for m in caplog.messages
619627
)
628+
# caplog.records collects from every logger propagating to root, so
629+
# filter to this library's before asserting the extra is present --
630+
# and default to None, not the expected id, so a record that is
631+
# *missing* the extra fails rather than passing vacuously (which is
632+
# precisely the regression -- a dropped LoggerAdapter -- this guards).
633+
session_records = [r for r in caplog.records if r.name.startswith("openjd.sessions")]
634+
assert session_records
620635
assert all(
621-
getattr(r, "session_id", self.DEADLINE_SHAPED_ID) == self.DEADLINE_SHAPED_ID
622-
for r in caplog.records
636+
getattr(r, "session_id", None) == self.DEADLINE_SHAPED_ID for r in session_records
623637
)
624638

625639
@pytest.mark.usefixtures("tmp_path") # built-in fixture
@@ -662,7 +676,7 @@ def test_concurrent_sessions_in_one_root_get_distinct_directories(self, tmp_path
662676
job_params = dict[str, ParameterValue]()
663677
sessions: list[Session] = []
664678
lock = threading.Lock()
665-
errors: list[BaseException] = []
679+
errors: list[Exception] = []
666680

667681
def make_session() -> None:
668682
try:
@@ -671,7 +685,7 @@ def make_session() -> None:
671685
job_parameter_values=job_params,
672686
session_root_directory=tmp_path,
673687
)
674-
except BaseException as err: # pragma: nocover - only on a failure
688+
except Exception as err: # pragma: nocover - only on a failure
675689
with lock:
676690
errors.append(err)
677691
return

0 commit comments

Comments
 (0)