Skip to content

Commit d7f6b55

Browse files
Merge pull request #1383 from datajoint/fix/jobs-semantic-check-1379
fix: Disable semantic_check for job table subtraction in refresh()
2 parents 39f4aa7 + 443bea1 commit d7f6b55

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

src/datajoint/jobs.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import platform
1414
import subprocess
1515

16-
from .condition import AndList
16+
from .condition import AndList, Not
1717
from .errors import DataJointError, DuplicateError
1818
from .heading import Heading
1919
from .table import Table
@@ -370,8 +370,6 @@ def refresh(
370370

371371
# Keys that need jobs: in key_source, not in target, not in jobs
372372
# Disable semantic_check for Job table (self) because its attributes may not have matching lineage
373-
from .condition import Not
374-
375373
new_keys = (key_source - self._target).restrict(Not(self), semantic_check=False).proj()
376374
new_key_list = new_keys.keys()
377375

@@ -395,8 +393,10 @@ def refresh(
395393
# 2. Re-pend success jobs if keep_completed=True
396394
if config.jobs.keep_completed:
397395
# Success jobs whose keys are in key_source but not in target
398-
# Disable semantic_check for Job table operations
399-
success_to_repend = self.completed.restrict(key_source, semantic_check=False) - self._target
396+
# Disable semantic_check for Job table operations (job table PK has different lineage than target)
397+
success_to_repend = self.completed.restrict(key_source, semantic_check=False).restrict(
398+
Not(self._target), semantic_check=False
399+
)
400400
repend_keys = success_to_repend.keys()
401401
for key in repend_keys:
402402
(self & key).delete_quick()

tests/integration/test_jobs.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,52 @@ def test_long_error_stack(clean_jobs, subject, experiment):
158158
experiment.jobs.error(key, "error message", long_error_stack)
159159
error_stack = experiment.jobs.errors.fetch1("error_stack")
160160
assert error_stack == long_error_stack, "error stacks do not agree"
161+
162+
163+
def test_populate_reserve_jobs_with_keep_completed(clean_jobs, subject, experiment):
164+
"""Test populate(reserve_jobs=True) with keep_completed=True.
165+
166+
Regression test for https://github.com/datajoint/datajoint-python/issues/1379
167+
The bug was that the `-` operator in jobs.refresh() didn't pass semantic_check=False,
168+
causing a DataJointError about different lineages when keep_completed=True.
169+
"""
170+
# Clear experiment data to ensure there's work to do
171+
experiment.delete()
172+
173+
with dj.config.override(jobs={"keep_completed": True, "add_job_metadata": True}):
174+
# Should not raise DataJointError about semantic matching
175+
experiment.populate(reserve_jobs=True)
176+
177+
# Verify jobs completed successfully
178+
assert len(experiment) > 0, "No data was populated"
179+
assert len(experiment.jobs.errors) == 0, "Unexpected errors during populate"
180+
181+
# With keep_completed=True, completed jobs should be retained
182+
assert len(experiment.jobs.completed) > 0, "Completed jobs not retained"
183+
184+
185+
def test_jobs_refresh_with_keep_completed(clean_jobs, subject, experiment):
186+
"""Test that jobs.refresh() works with keep_completed=True.
187+
188+
Regression test for https://github.com/datajoint/datajoint-python/issues/1379
189+
"""
190+
# Clear experiment data and jobs
191+
experiment.delete()
192+
experiment.jobs.delete()
193+
194+
with dj.config.override(jobs={"keep_completed": True, "add_job_metadata": True}):
195+
# Refresh should create pending jobs without semantic matching error
196+
experiment.jobs.refresh()
197+
pending_before = len(experiment.jobs.pending)
198+
assert pending_before > 0, "No pending jobs created"
199+
200+
# Manually reserve and complete a job
201+
key = experiment.jobs.pending.keys(limit=1)[0]
202+
experiment.jobs.reserve(key)
203+
experiment.jobs.complete(key)
204+
205+
# Job should now be completed
206+
assert len(experiment.jobs.completed) == 1, "Job not marked as completed"
207+
208+
# Calling refresh again should not raise semantic matching error
209+
experiment.jobs.refresh() # This was failing before the fix

0 commit comments

Comments
 (0)