Skip to content

Commit e082bde

Browse files
authored
Merge branch 'develop' into bugfix/warn-on-filter-expr-errors
2 parents 20753a5 + 854c5fb commit e082bde

File tree

11 files changed

+70
-25
lines changed

11 files changed

+70
-25
lines changed

.github/workflows/main.yml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
runs-on: ubuntu-latest
88
strategy:
99
matrix:
10-
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
10+
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
1111
steps:
1212
- uses: actions/checkout@v4
1313
- name: Set up Python ${{ matrix.python-version }}
@@ -48,12 +48,15 @@ jobs:
4848
4949
unittest-macos:
5050
runs-on: macos-latest
51+
strategy:
52+
matrix:
53+
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
5154
steps:
5255
- uses: actions/checkout@v4
53-
- name: Set up Python 3.9
56+
- name: Set up Python ${{ matrix.python-version }}
5457
uses: actions/setup-python@v5
5558
with:
56-
python-version: 3.9
59+
python-version: ${{ matrix.python-version }}
5760
- name: Install dependencies
5861
run: |
5962
./bootstrap.sh
@@ -115,12 +118,15 @@ jobs:
115118

116119
wheelvalidation:
117120
runs-on: ubuntu-latest
121+
strategy:
122+
matrix:
123+
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
118124
steps:
119125
- uses: actions/checkout@v4
120-
- name: Setup up Python 3.8
126+
- name: Setup up Python ${{ matrix.python-version }}
121127
uses: actions/setup-python@v5
122128
with:
123-
python-version: 3.8
129+
python-version: ${{ matrix.python-version }}
124130
- name: Generate Wheel
125131
run: |
126132
python -m pip install --upgrade pip setuptools build
@@ -137,7 +143,7 @@ jobs:
137143
runs-on: ubuntu-latest
138144
strategy:
139145
matrix:
140-
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
146+
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
141147
steps:
142148
- uses: actions/checkout@v4
143149
- name: Set up Python ${{ matrix.python-version }}

reframe/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77
import sys
88

9-
VERSION = '4.7.0-dev.9'
9+
VERSION = '4.7.0-dev.10'
1010
INSTALL_PREFIX = os.path.normpath(
1111
os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
1212
)

reframe/core/config.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,8 +447,10 @@ def validate(self):
447447
try:
448448
jsonschema.validate(site_config, self._schema)
449449
except jsonschema.ValidationError as e:
450-
raise ConfigError(f"could not validate configuration files: "
451-
f"'{self._sources}'") from e
450+
getlogger().debug(str(e))
451+
sources = ', '.join(f'`{f}`' for f in self._sources)
452+
raise ConfigError('could not validate configuration files: '
453+
f'{sources}') from e
452454

453455
def _warn_variables(config, opt_path):
454456
opt_path = '/'.join(opt_path + ['variables'])

reframe/core/exceptions.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#
99

1010
import inspect
11+
import jsonschema
1112
import os
1213

1314
import reframe
@@ -54,7 +55,10 @@ def message(self):
5455
def __str__(self):
5556
ret = self._message or ''
5657
if self.__cause__ is not None:
57-
ret += ': ' + str(self.__cause__)
58+
if isinstance(self.__cause__, jsonschema.ValidationError):
59+
ret += ': ' + self.__cause__.message
60+
else:
61+
ret += ': ' + str(self.__cause__)
5862

5963
return ret
6064

reframe/core/schedulers/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def filter_nodes_by_state(nodelist, state):
160160
:arg state: The state of the nodes.
161161
If ``all``, the initial list is returned untouched.
162162
If ``avail``, only the available nodes will be returned.
163-
All other values are interpretes as a state string.
163+
All other values are interpreted as a state string.
164164
State match is exclusive unless the ``*`` is added at the end of the
165165
state string.
166166
:returns: the filtered node list
@@ -169,7 +169,7 @@ def filter_nodes_by_state(nodelist, state):
169169
nodelist = {n for n in nodelist if n.is_avail()}
170170
elif state != 'all':
171171
if state.endswith('*'):
172-
# non-exclusive stat match
172+
# non-exclusive state match
173173
state = state[:-1]
174174
nodelist = {
175175
n for n in nodelist if n.in_state(state)
@@ -606,7 +606,15 @@ def guess_num_tasks(self):
606606
available_nodes = filter_nodes_by_state(
607607
available_nodes, self.sched_flex_alloc_nodes.lower()
608608
)
609+
getlogger().debug(
610+
f'[F] Total available in state='
611+
f'{self.sched_flex_alloc_nodes.lower()}: {len(available_nodes)}'
612+
)
609613
available_nodes = self.scheduler.filternodes(self, available_nodes)
614+
getlogger().debug(
615+
f'[F] Total available after scheduler filter: '
616+
f'{len(available_nodes)}'
617+
)
610618
return len(available_nodes) * num_tasks_per_node
611619

612620
def submit(self):

reframe/core/schedulers/slurm.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,21 @@ def _get_default_partition(self):
310310

311311
return None
312312

313+
def _get_actual_partition(self, options):
314+
try:
315+
completed = _run_strict(
316+
' '.join(['srun'] + options + ['--test-only', 'true'])
317+
)
318+
partition_match = re.search(r'partition (?P<partition>\S+)\s+',
319+
completed.stderr)
320+
if partition_match:
321+
return partition_match.group('partition')
322+
323+
except SpawnedProcessError as e:
324+
self.log('could not retrieve actual partition')
325+
326+
return None
327+
313328
def _merge_files(self, job):
314329
with osext.change_dir(job.workdir):
315330
out_glob = glob.glob(job.stdout + '_*')
@@ -345,13 +360,21 @@ def filternodes(self, job, nodes):
345360
if reservation:
346361
reservation = reservation.strip()
347362
nodes &= self._get_reservation_nodes(reservation)
348-
self.log(f'[F] Filtering nodes by reservation {reservation}: '
349-
f'available nodes now: {len(nodes)}')
363+
else:
364+
nodes = {node for node in nodes if not node.in_state('RESERVED')}
365+
366+
self.log(f'[F] Filtering nodes by reservation={reservation}: '
367+
f'available nodes now: {len(nodes)}')
350368

351369
if partitions:
352370
partitions = set(partitions.strip().split(','))
353371
else:
354-
default_partition = self._get_default_partition()
372+
# Use a default partition if one is configured. Otherwise,
373+
# fallback to the partition Slurm chooses for this set of options.
374+
default_partition = (
375+
self._get_default_partition() or
376+
self._get_actual_partition(options)
377+
)
355378
partitions = {default_partition} if default_partition else set()
356379
self.log(
357380
f'[F] No partition specified; using {default_partition!r}'
@@ -676,8 +699,13 @@ def in_statex(self, state):
676699
return self._states == set(state.upper().split('+'))
677700

678701
def is_avail(self):
679-
return any(self.in_statex(s)
680-
for s in ('ALLOCATED', 'COMPLETING', 'IDLE'))
702+
available_states = {
703+
'ALLOCATED',
704+
'COMPLETING',
705+
'IDLE',
706+
'RESERVED',
707+
}
708+
return self._states <= available_states
681709

682710
def is_down(self):
683711
return not self.is_avail()

reframe/frontend/autodetect.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ def _load_info(filename, schema=None):
106106
)
107107
return {}
108108
except jsonschema.ValidationError as e:
109+
getlogger().debug(str(e))
109110
raise ConfigError(
110111
f'could not validate meta-config file {filename!r}'
111112
) from e

reframe/frontend/printer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ def _print_failure_info(rec, runid, total_runs):
124124
self.info(f" * Description: {rec['descr']}")
125125
self.info(f" * System partition: {rec['system']}")
126126
self.info(f" * Environment: {rec['environ']}")
127+
self.info(f" * Test file: {rec['filename']}")
127128
self.info(f" * Stage directory: {rec['stagedir']}")
128129
self.info(f" * Node list: "
129130
f"{nodelist_abbrev(rec['job_nodelist'])}")

reframe/frontend/reporting/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,12 @@ def _restore_session(filename):
215215
except KeyError:
216216
found_ver = 'n/a'
217217

218-
getlogger().verbose(f'JSON validation error: {e}')
218+
getlogger().debug(str(e))
219219
raise ReframeError(
220220
f'failed to validate report {filename!r}: {e.args[0]} '
221221
f'(check report data version: required {DATA_VERSION}, '
222222
f'found: {found_ver})'
223-
) from None
223+
) from e
224224

225225
return _RestoredSessionInfo(report)
226226

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ classifiers =
1616
Programming Language :: Python :: 3.10
1717
Programming Language :: Python :: 3.11
1818
Programming Language :: Python :: 3.12
19+
Programming Language :: Python :: 3.13
1920
License :: OSI Approved :: BSD License
2021
Operating System :: MacOS
2122
Operating System :: POSIX :: Linux

0 commit comments

Comments
 (0)