Repository: BAMWelDX/weldx

Issue:
find latest supported tag from pattern
It would be helpful to have a utility function that can find the latest version of a supported tag in the loaded `WeldxExtensions` (identified by a pattern using `uri_match`)

Discussion:
pep8speaks: Hello @CagtayFabry! Thanks for updating this PR. 

* In the file [`weldx/asdf/util.py`](https://github.com/BAMWelDX/weldx/blob/b6d5f1635ea7bc5bedd28eb68fb0bb660ce01a1b/weldx/asdf/util.py):

> [Line 1:5](https://github.com/BAMWelDX/weldx/blob/b6d5f1635ea7bc5bedd28eb68fb0bb660ce01a1b/weldx/asdf/util.py#L1): [E999](https://duckduckgo.com/?q=pep8%20E999) SyntaxError: invalid syntax




codecov[bot]: # [Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/523?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX) Report
> Merging [#523](https://codecov.io/gh/BAMWelDX/weldx/pull/523?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX) (ac372bc) into [master](https://codecov.io/gh/BAMWelDX/weldx/commit/bd792983f5b7d21f9819e24fac2225a8073e38e0?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX) (bd79298) will **increase** coverage by `0.01%`.
> The diff coverage is `95.23%`.

> :exclamation: Current head ac372bc differs from pull request most recent head b6d5f16. Consider uploading reports for the commit b6d5f16 to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/BAMWelDX/weldx/pull/523/graphs/tree.svg?width=650&height=150&src=pr&token=wdof1qQTsn&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX)](https://codecov.io/gh/BAMWelDX/weldx/pull/523?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX)

```diff
@@            Coverage Diff             @@
##           master     #523      +/-   ##
==========================================
+ Coverage   95.11%   95.12%   +0.01%     
==========================================
  Files          93       93              
  Lines        5583     5603      +20     
==========================================
+ Hits         5310     5330      +20     
  Misses        273      273              
```


| [Impacted Files](https://codecov.io/gh/BAMWelDX/weldx/pull/523?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX) | Coverage Δ | |
|---|---|---|
| [weldx/asdf/util.py](https://codecov.io/gh/BAMWelDX/weldx/pull/523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX#diff-d2VsZHgvYXNkZi91dGlsLnB5) | `86.45% <95.23%> (+1.57%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/523?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/523?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX). Last update [bd79298...b6d5f16](https://codecov.io/gh/BAMWelDX/weldx/pull/523?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f0c5b92..aa32fec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
   [[#469]](https://github.com/BAMWelDX/weldx/pull/469).
 - added classes and functions at the top-level of the package to the documentation
   [[#437]](https://github.com/BAMWelDX/weldx/pulls/437).
+- added `weldx.asdf.util.get_highest_tag_version` utility function [[#523]](https://github.com/BAMWelDX/weldx/pull/523).
 
 ### removed
 
@@ -50,7 +51,6 @@
 - `WeldxFile.copy` now creates a copy to a (optional) file. Before it just returned a dictionary
   [[#504]](https://github.com/BAMWelDX/weldx/pull/504).
 
-
 ### fixes
 
 - `WeldxFile.show_asdf_header` prints output on console, before it only returned the header as parsed dict and string
diff --git a/doc/nitpick_ignore b/doc/nitpick_ignore
index 11eaa47..4dee933 100644
--- a/doc/nitpick_ignore
+++ b/doc/nitpick_ignore
@@ -74,6 +74,7 @@ py:meth scipy.stats.special_ortho_group
 py:obj mrp[i]
 
 # asdf
+py:class asdf.asdf.SerializationContext
 py:class iterable of asdf.extension.Compressor instances
 py:class See the class docstring for details on keyword
 py:class iterable of str
diff --git a/weldx/asdf/util.py b/weldx/asdf/util.py
index 7f1355e..9f59c2a 100644
--- a/weldx/asdf/util.py
+++ b/weldx/asdf/util.py
@@ -1,4 +1,5 @@
 """Utilities for asdf files."""
+from distutils.version import LooseVersion
 from io import BytesIO
 from pathlib import Path
 from typing import Any, Callable, Dict, List, Tuple, Type, Union
@@ -6,10 +7,14 @@ from warnings import warn
 
 import asdf
 from asdf.asdf import SerializationContext
+from asdf.config import AsdfConfig, get_config
+from asdf.extension._extension import Extension
 from asdf.tagged import TaggedDict
 from asdf.util import uri_match as asdf_uri_match
 from boltons.iterutils import get_path
 
+from weldx.asdf.constants import SCHEMA_PATH, WELDX_EXTENSION_URI
+from weldx.asdf.types import WeldxConverter
 from weldx.types import (
     SupportsFileReadOnly,
     SupportsFileReadWrite,
@@ -19,9 +24,6 @@ from weldx.types import (
 )
 from weldx.util import deprecated
 
-from .constants import SCHEMA_PATH, WELDX_EXTENSION_URI
-from .types import WeldxConverter
-
 _USE_WELDX_FILE = False
 _INVOKE_SHOW_HEADER = False
 
@@ -435,12 +437,16 @@ def dataclass_serialization_class(
     return _SerializationClass
 
 
-def get_weldx_extension(ctx: asdf.asdf.SerializationContext):
+def get_weldx_extension(ctx: Union[SerializationContext, AsdfConfig]) -> Extension:
     """Grab the weldx extension from list of current active extensions."""
+    if isinstance(ctx, asdf.asdf.SerializationContext):
+        extensions = ctx.extension_manager.extensions
+    elif isinstance(ctx, asdf.config.AsdfConfig):
+        extensions = ctx.extensions
+    else:
+        raise TypeError(f"unsupported context {ctx=}")
     extensions = [
-        ext
-        for ext in ctx.extension_manager.extensions
-        if str(ext.extension_uri) == WELDX_EXTENSION_URI
+        ext for ext in extensions if str(ext.extension_uri) == WELDX_EXTENSION_URI
     ]
     if not len(extensions) == 1:
         raise ValueError("Could not determine correct weldx extension.")
@@ -470,6 +476,52 @@ def get_converter_for_tag(tag: str) -> Union[type, None]:
     return None
 
 
+def get_highest_tag_version(
+    pattern: str, ctx: Union[SerializationContext, AsdfConfig] = None
+) -> Union[str, None]:
+    """Get the highest available weldx extension tag version matching a pattern.
+
+    Parameters
+    ----------
+    pattern
+        The tag pattern to match against.
+    ctx
+        The asdf context containing the extension.
+        Will look in the current ``asdf_config()`` by default.
+
+    Returns
+    -------
+    str
+        The full tag string of the highest version match.
+
+    Raises
+    ------
+    ValueError
+        When the pattern matches multiple base tags in in the extension.
+
+    Examples
+    --------
+    >>> from weldx.asdf.util import get_highest_tag_version
+    >>> get_highest_tag_version("asdf://weldx.bam.de/weldx/tags/uuid-*")
+    'asdf://weldx.bam.de/weldx/tags/uuid-1.0.0'
+
+    """
+    if ctx is None:
+        ctx = get_config()
+
+    extension = get_weldx_extension(ctx)
+
+    tags = [t._tag_uri for t in extension.tags if uri_match(pattern, t._tag_uri)]
+    if not tags:  # no match found
+        return None
+
+    tags.sort(key=LooseVersion)
+    base_tag = tags[-1].rpartition("-")[0]
+    if not all(t.startswith(base_tag) for t in tags):
+        raise ValueError("Found more than one base tag for pattern.")
+    return tags[-1]
+
+
 def _get_instance_shape(
     instance_dict: Union[TaggedDict, Dict[str, Any]]
 ) -> Union[List[int], None]:


Test patch:
diff --git a/weldx/tests/asdf_tests/test_asdf_util.py b/weldx/tests/asdf_tests/test_asdf_util.py
index f2e0130..b3122d5 100644
--- a/weldx/tests/asdf_tests/test_asdf_util.py
+++ b/weldx/tests/asdf_tests/test_asdf_util.py
@@ -8,6 +8,7 @@ import pytest
 from weldx import WeldxFile
 from weldx.asdf.util import (
     dataclass_serialization_class,
+    get_highest_tag_version,
     get_yaml_header,
     read_buffer,
     write_buffer,
@@ -154,3 +155,15 @@ def test_write_buffer_dummy_inline_arrays():
     restored = read_buffer(buff)[name]
     assert restored.dtype == array.dtype
     assert restored.shape == array.shape
+
+
+def test_get_highest_tag_version():
+    """Test getting some tags from the WeldxExtension."""
+    assert (
+        get_highest_tag_version("asdf://weldx.bam.de/weldx/tags/uuid-*")
+        == "asdf://weldx.bam.de/weldx/tags/uuid-1.0.0"
+    )
+    assert get_highest_tag_version("asdf://weldx.bam.de/weldx/tags/uuid-2.*") is None
+
+    with pytest.raises(ValueError):
+        get_highest_tag_version("asdf://weldx.bam.de/weldx/tags/**-*")