From f7170a0f3c53c12f0ecf87f5b7e63668dbd0d047 Mon Sep 17 00:00:00 2001
From: hoangtungdinh <11166240+hoangtungdinh@users.noreply.github.com>
Date: Thu, 31 Oct 2024 15:45:29 +0100
Subject: [PATCH 1/4] Add the concept of last supported version
Signed-off-by: hoangtungdinh <11166240+hoangtungdinh@users.noreply.github.com>
---
README.md | 13 +-
...tions_connection_one_connection_element.py | 1 +
qc_opendrive/main.py | 24 +-
...n_one_connection_element_v1_8_0_valid.xodr | 410 ++++++++++++++++++
tests/test_semantic_checks.py | 26 ++
5 files changed, 467 insertions(+), 7 deletions(-)
create mode 100755 tests/data/junctions_connection_one_connection_element/junctions_connection_one_connection_element_v1_8_0_valid.xodr
diff --git a/README.md b/README.md
index be80306..aab6f7c 100644
--- a/README.md
+++ b/README.md
@@ -279,12 +279,13 @@ Contributions of valid and invalid OpenDrive sample files are also welcome. New
1. Create a new Python module for each checker.
2. Specify the following global variables for the Python module
-| Variable | Meaning |
-| --- | --- |
-| `CHECKER_ID` | The ID of the checker |
-| `CHECKER_DESCRIPTION` | The description of the checker |
-| `CHECKER_PRECONDITIONS` | A set of other checkers in which if any of them raise an issue, the current checker will be skipped |
-| `RULE_UID` | The rule UID of the rule that the checker will check |
+| Variable | Presence | Meaning |
+| --- | --- | --- |
+| `CHECKER_ID` | Required | The ID of the checker |
+| `CHECKER_DESCRIPTION` | Required | The description of the checker |
+| `CHECKER_PRECONDITIONS` | Required | A set of other checkers in which if any of them raise an issue, the current checker will be skipped |
+| `RULE_UID` | Required | The rule UID of the rule that the checker will check |
+| `LAST_SUPPORTED_VERSION` | Optional | The last supported version of the standard. If the input file has a version higher than the last supported version, the checker will be skipped. Do not define this variable if the checker does not have a last supported version. |
3. Implement the checker logic in the following function:
diff --git a/qc_opendrive/checks/semantic/junctions_connection_one_connection_element.py b/qc_opendrive/checks/semantic/junctions_connection_one_connection_element.py
index ead92e3..a738e36 100644
--- a/qc_opendrive/checks/semantic/junctions_connection_one_connection_element.py
+++ b/qc_opendrive/checks/semantic/junctions_connection_one_connection_element.py
@@ -19,6 +19,7 @@
CHECKER_DESCRIPTION = "Each connecting road shall be represented by exactly one element. A connecting road may contain as many lanes as required."
CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS
RULE_UID = "asam.net:xodr:1.7.0:junctions.connection.one_connection_element"
+LAST_SUPPORTED_VERSION = "1.7.0"
def _check_junctions_connection_one_connection_element(
diff --git a/qc_opendrive/main.py b/qc_opendrive/main.py
index 65c1091..b6cd0a7 100644
--- a/qc_opendrive/main.py
+++ b/qc_opendrive/main.py
@@ -73,7 +73,7 @@ def execute_checker(
return
- # Checker definition setting. If not satisfied then set status as SKIPPED and return
+ # Check definition setting. If not satisfied then set status as SKIPPED and return
if required_definition_setting:
schema_version = checker_data.schema_version
@@ -100,6 +100,28 @@ def execute_checker(
return
+ # Check last supported version. If not satisfied then set status as SKIPPED and return
+ if hasattr(checker, "LAST_SUPPORTED_VERSION"):
+ schema_version = checker_data.schema_version
+ if (
+ schema_version is None
+ or utils.compare_versions(schema_version, checker.LAST_SUPPORTED_VERSION)
+ > 0
+ ):
+ checker_data.result.set_checker_status(
+ checker_bundle_name=constants.BUNDLE_NAME,
+ checker_id=checker.CHECKER_ID,
+ status=StatusType.SKIPPED,
+ )
+
+ checker_data.result.add_checker_summary(
+ constants.BUNDLE_NAME,
+ checker.CHECKER_ID,
+ f"Version {schema_version} is higher than the last supported version {checker.LAST_SUPPORTED_VERSION}. Skip the check.",
+ )
+
+ return
+
# Execute checker
try:
checker.check_rule(checker_data)
diff --git a/tests/data/junctions_connection_one_connection_element/junctions_connection_one_connection_element_v1_8_0_valid.xodr b/tests/data/junctions_connection_one_connection_element/junctions_connection_one_connection_element_v1_8_0_valid.xodr
new file mode 100755
index 0000000..390cb02
--- /dev/null
+++ b/tests/data/junctions_connection_one_connection_element/junctions_connection_one_connection_element_v1_8_0_valid.xodr
@@ -0,0 +1,410 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/test_semantic_checks.py b/tests/test_semantic_checks.py
index 7ffeafa..304b51f 100644
--- a/tests/test_semantic_checks.py
+++ b/tests/test_semantic_checks.py
@@ -496,9 +496,10 @@ def test_junctions_connection_one_connection_element(
"target_file,issue_count,issue_xpath",
[
("v1_8_0_valid", 0, []),
+ ("v1_6_0_skipped", 0, []),
],
)
-def test_junctions_connection_one_connection_element_last_supported_version(
+def test_junctions_connection_one_connection_element_applicable_version(
target_file: str,
issue_count: int,
issue_xpath: List[str],
diff --git a/tests/test_version.py b/tests/test_version.py
new file mode 100644
index 0000000..ac814dc
--- /dev/null
+++ b/tests/test_version.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: MPL-2.0
+# Copyright 2024, ASAM e.V.
+# This Source Code Form is subject to the terms of the Mozilla
+# Public License, v. 2.0. If a copy of the MPL was not distributed
+# with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+import pytest
+from qc_opendrive import version as qc_version
+
+
+@pytest.mark.parametrize(
+ "applicable_version,has_lower_bound",
+ [
+ (">1.0.0", True),
+ (">1.0.0,<2.0.0", True),
+ (">1.0.0,<=2.0.0", True),
+ (">=1.0.0", True),
+ (">=1.0.0,<2.0.0", True),
+ (">=1.0.0,<=2.0.0", True),
+ (">=1.0.0,>=1.0.1,<=2.0.0", True),
+ (">=1.0.0,<=1.0.1,<=2.0.0", True),
+ ("<=2.0.0", False),
+ ("<2.0.0", False),
+ ("<=2.0.0,<=3.0.0", False),
+ ("<2.0.0,<3.0.0", False),
+ ],
+)
+def test_has_lower_bound(applicable_version: str, has_lower_bound: bool) -> None:
+ assert qc_version.has_lower_bound(applicable_version) == has_lower_bound
+
+
+@pytest.mark.parametrize(
+ "version,applicable_version,match",
+ [
+ ("1.7.0", ">=1.7.0", True),
+ ("1.7.0", "<=1.7.0", True),
+ ("1.7.0", ">1.7.0", False),
+ ("1.7.0", "<1.7.0", False),
+ ("1.7.0", "<1.7.0,<1.8.0", False),
+ ("1.7.0", ">1.7.0,>1.6.0", False),
+ ("1.7.0", ">=1.7.0,>1.6.0", True),
+ ("1.7.0", ">=1.7.0,>1.8.0", False),
+ ("1.7.0", "<=1.7.0,>1.8.0", False),
+ ("1.7.0", "<=1.7.0,<1.8.0", True),
+ ("1.7.0", "<=1.7.0,<1.6.0", False),
+ ],
+)
+def test_match(version: str, applicable_version: str, match: bool) -> None:
+ assert qc_version.match(version, applicable_version) == match
+
+
+@pytest.mark.parametrize(
+ "version_expression,is_valid",
+ [
+ ("1.7.0", False),
+ (">1.7.0", True),
+ (">=1.7.0", True),
+ ("<1.7.0", True),
+ ("<=1.7.0", True),
+ ("==1.7.0", False),
+ ("!=1.7.0", False),
+ ("<1.7", False),
+ ],
+)
+def test_is_valid_version_expression(version_expression: str, is_valid: bool) -> None:
+ assert qc_version.is_valid_version_expression(version_expression) == is_valid
From 0d18e5c9d2ae345edcf7f5196dde7cb3770c4f81 Mon Sep 17 00:00:00 2001
From: hoangtungdinh <11166240+hoangtungdinh@users.noreply.github.com>
Date: Thu, 7 Nov 2024 16:34:25 +0100
Subject: [PATCH 3/4] Address reviews
Signed-off-by: hoangtungdinh <11166240+hoangtungdinh@users.noreply.github.com>
---
README.md | 34 +++++++++---------
qc_opendrive/main.py | 21 +++++------
qc_opendrive/version.py | 77 ++++++++++++++++++++++++++---------------
tests/test_version.py | 2 ++
4 files changed, 76 insertions(+), 58 deletions(-)
diff --git a/README.md b/README.md
index 6e4d76c..2a789f0 100644
--- a/README.md
+++ b/README.md
@@ -285,7 +285,7 @@ Contributions of valid and invalid OpenDrive sample files are also welcome. New
| `CHECKER_DESCRIPTION` | Required | The description of the checker |
| `CHECKER_PRECONDITIONS` | Required | A set of other checkers in which if any of them raise an issue, the current checker will be skipped |
| `RULE_UID` | Required | The rule UID of the rule that the checker will check |
-| `APPLICABLE_VERSION` | Optional | An optional variable to define extra constraints on the applicable version. See details below. |
+| `APPLICABLE_VERSIONS` | Optional | An optional variable to define extra constraints on the applicable version. See details below. |
1. Implement the checker logic in the following function:
@@ -306,39 +306,39 @@ def run_checks(config: Configuration, result: Result) -> None:
All the checkers in this checker bundle are implemented in this way. Take a look at some of them before implementing your first checker.
-**A note on `APPLICABLE_VERSION`**
+**A note on `APPLICABLE_VERSIONS`**
-The `APPLICABLE_VERSION` variable can be used to define additional constraints on the versions of the input files that a rule supports, in addition to the **definition setting** in the rule UID. It can be specified in the same way as the [Python Version Specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/#id5). For example:
+The `APPLICABLE_VERSIONS` variable can be used to define additional constraints on the versions of the input files that a rule supports, in addition to the **definition setting** in the rule UID. It can be specified in the same way as the [Python Version Specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/#id5). For example:
```python
-APPLICABLE_VERSION = "<1.8.0"
-APPLICABLE_VERSION = ">=1.6.0"
-APPLICABLE_VERSION = "<1.8.0,>=1.6.0"
+APPLICABLE_VERSIONS = "<1.8.0"
+APPLICABLE_VERSIONS = ">=1.6.0"
+APPLICABLE_VERSIONS = "<1.8.0,>=1.6.0"
```
-The specification consists of a series of version clauses, separated by commas. The comma is equivalent to a logical "AND" operator: a candidate version must match all given version clauses in order to match the **applicable version** as a whole.
+The specification consists of a series of version clauses, separated by commas. The comma is equivalent to a logical "AND" operator: a candidate version must match all given version clauses in order to match the **applicable versions** as a whole.
-The **applicable version** only supports versions of the form `major.minor.patch`. For example, `1.7.0rc1` is not supported, but `1.7.0` is supported.
+The **applicable versions** only supports versions of in the full semantic form `major.minor.patch`. Elision of `minor` or `patch` elements are not supported. For example, `1.7.0rc1` and `1.7` are not supported, but `1.7.0` is supported.
-The **applicable version** only supports the following comparison operators.
+The **applicable versions** only supports the following comparison operators.
* `<` smaller than **(upper bound)**
* `<=` smaller or equal than **(upper bound)**
* `>` greater than **(lower bound)**
* `>=` greater or equal than **(lower bound)**
-The **definition setting** in rule UID and the **applicable version** together define the versions of the input file in which a rule can be applied. For example, let's consider a rule UID for ASAM OpenDRIVE `asam.net:xodr:1.6.0:*`. The **definition setting** in this case is `1.6.0`.
+The **definition setting** in rule UID and the **applicable versions** together define the versions of the input file in which a rule can be applied. For example, let's consider a rule UID for ASAM OpenDRIVE `asam.net:xodr:1.6.0:*`. The **definition setting** in this case is `1.6.0`.
-1. If no **applicable version** is specified, a rule will be applied starting from the **definition setting** version, up to the most recent one.
- * For the example, if the `APPLICABLE_VERSION` variable does not exist, then the rule is applied to OpenDRIVE versions 1.6.0, 1.6.1, 1.7.0, 1.8.0: the internal representation of the version specifier is `>=1.6.0`.
+1. If no **applicable versions** is specified, a rule will be applied starting from the **definition setting** version, up to the most recent one.
+ * For the example, if the `APPLICABLE_VERSIONS` variable does not exist, then the rule is applied to OpenDRIVE versions 1.6.0, 1.6.1, 1.7.0, 1.8.0: the internal representation of the version specifier is `>=1.6.0`.
-2. If the **applicable version** is specified, and defines only **upper bounds**, then the **definition setting** defines the lower bound
- * For the example, if `APPLICABLE_VERSION = "<1.8.0"`, then the rule is applied to OpenDRIVE versions 1.6.0, 1.6.1, 1.7.0: the internal representation of the version specifier is `>=1.6.0,<1.8.0`.
+2. If the **applicable versions** is specified, and defines only **upper bounds**, then the **definition setting** defines the lower bound
+ * For the example, if `APPLICABLE_VERSIONS = "<1.8.0"`, then the rule is applied to OpenDRIVE versions 1.6.0, 1.6.1, 1.7.0: the internal representation of the version specifier is `>=1.6.0,<1.8.0`.
-3. If the **applicable version** is specified, and defines at least one **lower bound**, then the **definition setting** is ignored. Only the **lower bounds** defined in the **applicable version** are taken into account.
- * For the example, if `APPLICABLE_VERSION = ">=1.5.0"`, then the rule is applied to OpenDRIVE versions 1.5.0, 1.6.0, 1.6.1, 1.7.0, 1.8.0: the internal representation of the version specifier is `>=1.5.0`.
+3. If the **applicable versions** is specified, and defines at least one **lower bound**, then the **definition setting** is ignored. Only the **lower bounds** defined in the **applicable versions** are taken into account.
+ * For the example, if `APPLICABLE_VERSIONS = ">=1.5.0"`, then the rule is applied to OpenDRIVE versions 1.5.0, 1.6.0, 1.6.1, 1.7.0, 1.8.0: the internal representation of the version specifier is `>=1.5.0`.
-| Case Number | Example Rule | `APPLICABLE_VERSION` | Internal Representation | File versions to be checked |
+| Case Number | Example Rule | `APPLICABLE_VERSIONS` | Internal Representation | File versions to be checked |
|-------------|-------------------------|----------------------|----------------------------------|-----------------------------------|
| 1 | `asam.net:xodr:1.6.0:*` | `""` | `">=1.6.0"` | 1.6.0, 1.6.1, 1.7.0, 1.8.0 |
| 2 | `asam.net:xodr:1.6.0:*` | `"<1.8.0"` | `">=1.6.0,<1.8.0"` | 1.6.0, 1.6.1, 1.7.0 |
diff --git a/qc_opendrive/main.py b/qc_opendrive/main.py
index 6e3a7eb..89dd98c 100644
--- a/qc_opendrive/main.py
+++ b/qc_opendrive/main.py
@@ -9,6 +9,7 @@
import types
from qc_baselib import Configuration, Result, StatusType
+from qc_baselib.models.result import RuleType
from qc_opendrive import constants
from qc_opendrive import version
@@ -69,20 +70,14 @@ def check_version(checker: types.ModuleType, checker_data: models.CheckerData) -
"""
schema_version = checker_data.schema_version
- splitted_rule_uid = checker.RULE_UID.split(":")
- if len(splitted_rule_uid) != 4:
- raise RuntimeError(f"Invalid rule uid: {checker.RULE_UID}")
-
- definition_setting = splitted_rule_uid[2]
- definition_setting_expr = f">={definition_setting}"
+ rule_uid = RuleType(rule_uid=checker.RULE_UID)
+ definition_setting_expr = f">={rule_uid.definition_setting}"
match_definition_setting = version.match(schema_version, definition_setting_expr)
- applicable_version = (
- checker.APPLICABLE_VERSION if hasattr(checker, "APPLICABLE_VERSION") else None
- )
+ applicable_version = getattr(checker, "APPLICABLE_VERSION", "")
# Check whether applicable version specification is valid
- if applicable_version is not None and not version.is_valid_version_expression(
+ if applicable_version and not version.is_valid_version_expression(
applicable_version
):
checker_data.result.set_checker_status(
@@ -110,14 +105,14 @@ def check_version(checker: types.ModuleType, checker_data: models.CheckerData) -
checker_data.result.add_checker_summary(
constants.BUNDLE_NAME,
checker.CHECKER_ID,
- f"The definition setting {definition_setting} is not valid. Skip the check.",
+ f"The definition setting {rule_uid.definition_setting} is not valid. Skip the check.",
)
return False
match_applicable_version = (
version.match(schema_version, applicable_version)
- if applicable_version is not None
+ if applicable_version
else True
)
@@ -138,7 +133,7 @@ def check_version(checker: types.ModuleType, checker_data: models.CheckerData) -
return False
# Check definition setting if there is no applicable version or applicable version has no lower bound
- if applicable_version is None or not version.has_lower_bound(applicable_version):
+ if not version.has_lower_bound(applicable_version):
if not match_definition_setting:
checker_data.result.set_checker_status(
checker_bundle_name=constants.BUNDLE_NAME,
diff --git a/qc_opendrive/version.py b/qc_opendrive/version.py
index a255b23..5061b20 100644
--- a/qc_opendrive/version.py
+++ b/qc_opendrive/version.py
@@ -5,50 +5,52 @@
# with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
import re
-from typing import List, Optional
+from typing import List
from semver.version import Version
+_is_lower_bound_pattern = re.compile(r"^>")
+
+
def _is_lower_bound(expression: str) -> bool:
- pattern = r"^(>=|>)"
- match = re.match(pattern, expression)
- if match:
- return True
- else:
- return False
+ return bool(_is_lower_bound_pattern.match(expression))
-def _get_version_clause(applicable_version: str) -> bool:
- version_clauses = applicable_version.split(",")
- version_clauses = [clause.replace(" ", "") for clause in version_clauses]
- return version_clauses
+_re_split_clauses = re.compile(r"\s*,\s*")
+_re_remove_spaces = re.compile(r"\s+")
-def is_valid_version_expression(version_expression: str) -> bool:
- version_clauses = _get_version_clause(version_expression)
- pattern = r"^(>=|<=|>|<)(\d+)\.(\d+)\.(\d+)$"
- for clause in version_clauses:
- match = re.match(pattern, clause)
- if not match:
- return False
+def _get_version_clauses(applicable_versions: str) -> List[str]:
+ version_clauses = _re_split_clauses.split(applicable_versions)
+ return [_re_remove_spaces.sub("", vc) for vc in version_clauses]
- return True
+
+_is_valid_clause_pattern = re.compile(r"^([<>]=?)(\d+)\.(\d+)\.(\d+)$")
-def has_lower_bound(applicable_version: str) -> bool:
+def _is_valid_clause(clause: str) -> bool:
+ return bool(_is_valid_clause_pattern.match(clause))
+
+
+def is_valid_version_expression(version_expression: str) -> bool:
+ return all(
+ _is_valid_clause(clause) for clause in _get_version_clauses(version_expression)
+ )
+
+
+def has_lower_bound(applicable_versions: str) -> bool:
"""
Check if there is at least one lower bound in an applicable version string.
Example:
"<1.0.0,>0.0.1" returns True
"<1.0.0" returns False
"""
- expressions = applicable_version.split(",")
-
- for expr in expressions:
- if _is_lower_bound(expr):
- return True
-
- return False
+ return any(
+ (
+ _is_lower_bound(clause)
+ for clause in _get_version_clauses(applicable_versions)
+ )
+ )
def match(version: str, applicable_version: str) -> bool:
@@ -65,7 +67,7 @@ def match(version: str, applicable_version: str) -> bool:
:param version: The version to be checked.
:param applicable_version: Comma separated applicable version.
"""
- version_clauses = _get_version_clause(applicable_version)
+ version_clauses = _get_version_clauses(applicable_version)
parsed_version = Version.parse(version)
@@ -75,3 +77,22 @@ def match(version: str, applicable_version: str) -> bool:
return False
return True
+
+
+def match(version: str, applicable_versions: str) -> bool:
+ """
+ Check if the version is valid, given an applicable version.
+ Applicable version is comma separated. The comma acts as a logical AND.
+ A candidate version must match all given version clauses in order to match
+ the applicable_version as a whole.
+ The validity check follows the concept of Python version specifiers.
+ See: https://packaging.python.org/en/latest/specifications/version-specifiers/#id5
+
+ :param version: The version to be checked.
+ :param applicable_version: Comma separated applicable version. Invalid version clauses will force the check to fail
+ :return: a boolean for the match
+ """
+ version = Version.parse(version)
+ clauses = _get_version_clauses(applicable_versions)
+
+ return all(_is_valid_clause(clause) and version.match(clause) for clause in clauses)
diff --git a/tests/test_version.py b/tests/test_version.py
index ac814dc..78262b7 100644
--- a/tests/test_version.py
+++ b/tests/test_version.py
@@ -23,6 +23,7 @@
("<2.0.0", False),
("<=2.0.0,<=3.0.0", False),
("<2.0.0,<3.0.0", False),
+ ("", False),
],
)
def test_has_lower_bound(applicable_version: str, has_lower_bound: bool) -> None:
@@ -43,6 +44,7 @@ def test_has_lower_bound(applicable_version: str, has_lower_bound: bool) -> None
("1.7.0", "<=1.7.0,>1.8.0", False),
("1.7.0", "<=1.7.0,<1.8.0", True),
("1.7.0", "<=1.7.0,<1.6.0", False),
+ ("1.7.0", "<=1.7.0,<1.8", False),
],
)
def test_match(version: str, applicable_version: str, match: bool) -> None:
From d27f4146dd09237743721dbf6b2a71af5b16e0cb Mon Sep 17 00:00:00 2001
From: hoangtungdinh <11166240+hoangtungdinh@users.noreply.github.com>
Date: Fri, 8 Nov 2024 11:21:40 +0100
Subject: [PATCH 4/4] Remove redundant function and clean up
Signed-off-by: hoangtungdinh <11166240+hoangtungdinh@users.noreply.github.com>
---
qc_opendrive/main.py | 12 ++----------
qc_opendrive/version.py | 33 ++++-----------------------------
tests/test_version.py | 2 ++
3 files changed, 8 insertions(+), 39 deletions(-)
diff --git a/qc_opendrive/main.py b/qc_opendrive/main.py
index 89dd98c..502fa45 100644
--- a/qc_opendrive/main.py
+++ b/qc_opendrive/main.py
@@ -77,9 +77,7 @@ def check_version(checker: types.ModuleType, checker_data: models.CheckerData) -
applicable_version = getattr(checker, "APPLICABLE_VERSION", "")
# Check whether applicable version specification is valid
- if applicable_version and not version.is_valid_version_expression(
- applicable_version
- ):
+ if not version.is_valid_version_expression(applicable_version):
checker_data.result.set_checker_status(
checker_bundle_name=constants.BUNDLE_NAME,
checker_id=checker.CHECKER_ID,
@@ -110,14 +108,8 @@ def check_version(checker: types.ModuleType, checker_data: models.CheckerData) -
return False
- match_applicable_version = (
- version.match(schema_version, applicable_version)
- if applicable_version
- else True
- )
-
# First, check applicable version
- if not match_applicable_version:
+ if not version.match(schema_version, applicable_version):
checker_data.result.set_checker_status(
checker_bundle_name=constants.BUNDLE_NAME,
checker_id=checker.CHECKER_ID,
diff --git a/qc_opendrive/version.py b/qc_opendrive/version.py
index 5061b20..07b5157 100644
--- a/qc_opendrive/version.py
+++ b/qc_opendrive/version.py
@@ -22,14 +22,15 @@ def _is_lower_bound(expression: str) -> bool:
def _get_version_clauses(applicable_versions: str) -> List[str]:
version_clauses = _re_split_clauses.split(applicable_versions)
- return [_re_remove_spaces.sub("", vc) for vc in version_clauses]
+ version_clauses = [_re_remove_spaces.sub("", vc) for vc in version_clauses]
+ return [vc for vc in version_clauses if vc != ""]
-_is_valid_clause_pattern = re.compile(r"^([<>]=?)(\d+)\.(\d+)\.(\d+)$")
+_re_is_valid_clause = re.compile(r"^([<>]=?)(\d+)\.(\d+)\.(\d+)$")
def _is_valid_clause(clause: str) -> bool:
- return bool(_is_valid_clause_pattern.match(clause))
+ return bool(_re_is_valid_clause.match(clause))
def is_valid_version_expression(version_expression: str) -> bool:
@@ -53,32 +54,6 @@ def has_lower_bound(applicable_versions: str) -> bool:
)
-def match(version: str, applicable_version: str) -> bool:
- """
- Check if the version is valid, given an applicable version.
-
- Applicable version is comma separated. The comma acts as a logical AND.
- A candidate version must match all given version clauses in order to match
- the applicable_version as a whole.
-
- The validity check follows the concept of Python version specifiers.
- See: https://packaging.python.org/en/latest/specifications/version-specifiers/#id5
-
- :param version: The version to be checked.
- :param applicable_version: Comma separated applicable version.
- """
- version_clauses = _get_version_clauses(applicable_version)
-
- parsed_version = Version.parse(version)
-
- for clause in version_clauses:
- is_matched = parsed_version.match(clause)
- if not is_matched:
- return False
-
- return True
-
-
def match(version: str, applicable_versions: str) -> bool:
"""
Check if the version is valid, given an applicable version.
diff --git a/tests/test_version.py b/tests/test_version.py
index 78262b7..d01ccd0 100644
--- a/tests/test_version.py
+++ b/tests/test_version.py
@@ -45,6 +45,7 @@ def test_has_lower_bound(applicable_version: str, has_lower_bound: bool) -> None
("1.7.0", "<=1.7.0,<1.8.0", True),
("1.7.0", "<=1.7.0,<1.6.0", False),
("1.7.0", "<=1.7.0,<1.8", False),
+ ("1.7.0", "", True),
],
)
def test_match(version: str, applicable_version: str, match: bool) -> None:
@@ -62,6 +63,7 @@ def test_match(version: str, applicable_version: str, match: bool) -> None:
("==1.7.0", False),
("!=1.7.0", False),
("<1.7", False),
+ ("", True),
],
)
def test_is_valid_version_expression(version_expression: str, is_valid: bool) -> None: