diff --git a/CHANGELOG b/CHANGELOG index af69e68003f97d111619add0d1d2195f341a11ee..ef6a0eec16a30687b08a74820c6d5afef3f85187 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,9 @@ - #1373: Update installation instructions with `rsync`, and fix our `Dockerfile` adding `rsync` and `subversion`. - #1207: Add experiment path in `autosubmit describe`. +- #1397: Use `tini` as entrypoint in our Docker image. +- #1428: Use natural sort order for graph entries (adapted from Cylc), + enable doctests to be executed with pytest. 4.1.10 - Hotfix =============== diff --git a/autosubmit/helpers/utils.py b/autosubmit/helpers/utils.py index 7ccca9cf89a6e60664192a305baa158ce13aa0f5..6d13cb53b32215864e8036ec223a2689dbb6efe2 100644 --- a/autosubmit/helpers/utils.py +++ b/autosubmit/helpers/utils.py @@ -1,13 +1,15 @@ -import subprocess +import locale import os import pwd +import re import signal -import locale -from autosubmit.notifications.mail_notifier import MailNotifier - -from autosubmit.notifications.notifier import Notifier +import subprocess +from itertools import zip_longest from autosubmitconfigparser.config.basicconfig import BasicConfig + +from autosubmit.notifications.mail_notifier import MailNotifier +from autosubmit.notifications.notifier import Notifier from log.log import AutosubmitCritical, Log @@ -160,3 +162,87 @@ def restore_platforms(platform_to_test, mail_notify=False, as_conf=None, expid=N raise AutosubmitCritical("Issues while checking the connectivity of platforms.", 7010, issues + "\n" + ssh_config_issues) +# Source: https://github.com/cylc/cylc-flow/blob/a722b265ad0bd68bc5366a8a90b1dbc76b9cd282/cylc/flow/tui/util.py#L226 +class NaturalSort: + """An object to use as a sort key for sorting strings as a human would. + + This recognises numerical patterns within strings. + + Examples: + >>> N = NaturalSort + + String comparisons work as normal: + >>> N('') < N('') + False + >>> N('a') < N('b') + True + >>> N('b') < N('a') + False + + Integer comparisons work as normal: + >>> N('9') < N('10') + True + >>> N('10') < N('9') + False + + Integers rank higher than strings: + >>> N('1') < N('a') + True + >>> N('a') < N('1') + False + + Integers within strings are sorted numerically: + >>> N('a9b') < N('a10b') + True + >>> N('a10b') < N('a9b') + False + + Lexicographical rules apply when substrings match: + >>> N('a1b2') < N('a1b2c3') + True + >>> N('a1b2c3') < N('a1b2') + False + + Equality works as per regular string rules: + >>> N('a1b2c3') == N('a1b2c3') + True + + """ + + PATTERN = re.compile(r'(\d+)') + + def __init__(self, value): + self.value = tuple( + int(item) if item.isdigit() else item + for item in self.PATTERN.split(value) + # remove empty strings if value ends with a digit + if item + ) + + def __eq__(self, other): + return self.value == other.value + + def __lt__(self, other): + for this, that in zip_longest(self.value, other.value): + if this is None: + return True + if that is None: + return False + this_isstr = isinstance(this, str) + that_isstr = isinstance(that, str) + if this_isstr and that_isstr: + if this == that: + continue + return this < that + this_isint = isinstance(this, int) + that_isint = isinstance(that, int) + if this_isint and that_isint: + if this == that: + continue + return this < that + if this_isint and that_isstr: + return True + if this_isstr and that_isint: + return False + return False + diff --git a/autosubmit/monitor/monitor.py b/autosubmit/monitor/monitor.py index 224ee604ea5605dc8ad74bd5324352142426f1f4..ccdfffb020747045194c93cb37efe6abe6c7961c 100644 --- a/autosubmit/monitor/monitor.py +++ b/autosubmit/monitor/monitor.py @@ -34,6 +34,7 @@ import autosubmit.helpers.utils as HelperUtils from autosubmit.job.job_common import Status from autosubmit.job.job import Job +from autosubmit.helpers.utils import NaturalSort from autosubmitconfigparser.config.basicconfig import BasicConfig from autosubmitconfigparser.config.configcommon import AutosubmitConfig @@ -319,7 +320,7 @@ class Monitor: return self.nodes_plotted.add(job) if job.has_children() != 0: - for child in sorted(job.children, key=lambda k: k.name): + for child in sorted(job.children, key=lambda k: NaturalSort(k.name)): node_child, skip = self._check_node_exists( exp, child, groups, hide_groups) color, label = self._check_final_status(job, child) diff --git a/pytest.ini b/pytest.ini index 5ab9b5fe79b672e06788ce1f6d0c7d689cb2c882..32a1148ce9ab12559d5b06a6c7a3f9ad076ecd5b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -4,8 +4,10 @@ addopts = --cov=autosubmit --cov-config=.coveragerc --cov-report=html:test/htmlcov --cov-report=xml:test/coverage.xml --strict-markers + --doctest-modules testpaths = test/unit + autosubmit doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL