From fe3a1f2f5d55be30e76b5520d9606c959fe63020 Mon Sep 17 00:00:00 2001 From: KuechA <31155350+KuechA@users.noreply.github.com> Date: Fri, 7 Feb 2025 09:13:01 +0100 Subject: [PATCH] Python `VersionInfo` extension (#2032) --- .../python/PythonLanguageFrontend.kt | 28 ++++++---- .../cpg/frontends/python/SystemInformation.kt | 20 ++++++- .../frontends/python/SystemInformationTest.kt | 54 +++++++++++++++++++ 3 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/SystemInformationTest.kt diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt index 088985c59bd..b8c68dbbfcf 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt @@ -352,6 +352,22 @@ class PythonLanguageFrontend(language: Language, ctx: Tr } } +/** + * Returns the version info from the [TranslationConfiguration] as [VersionInfo] or `null` if it was + * not specified. + */ +val TranslationConfiguration.versionInfo: VersionInfo? + get() { + // We need to populate the version info "in-order", to ensure that we do not + // set the micro version if minor and major are not set, i.e., there must not be a + // "gap" in the granularity of version numbers + return this.symbols["PYTHON_VERSION_MAJOR"]?.toLong()?.let { major -> + val minor = this.symbols["PYTHON_VERSION_MINOR"]?.toLong() + val micro = if (minor != null) this.symbols["PYTHON_VERSION_MICRO"]?.toLong() else null + VersionInfo(major, minor, micro) + } + } + /** * Populate system information from defined symbols that represent our environment. We add it as an * overlay node to our [TranslationUnitDeclaration]. @@ -363,17 +379,7 @@ fun populateSystemInformation( var sysInfo = SystemInformation( platform = config.symbols["PYTHON_PLATFORM"], - // We need to populate the version info "in-order", to ensure that we do not - // set the micro version if minor and major are not set, i.e., there must not be a - // "gap" in the granularity of version numbers - versionInfo = - config.symbols["PYTHON_VERSION_MAJOR"]?.toLong()?.let { major -> - val minor = config.symbols["PYTHON_VERSION_MINOR"]?.toLong() - val micro = - if (minor != null) config.symbols["PYTHON_VERSION_MICRO"]?.toLong() - else null - VersionInfo(major, minor, micro) - }, + versionInfo = config.versionInfo, ) sysInfo.underlyingNode = tu return sysInfo diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/SystemInformation.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/SystemInformation.kt index 049a4c49224..23857f09a57 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/SystemInformation.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/SystemInformation.kt @@ -29,7 +29,7 @@ import de.fraunhofer.aisec.cpg.graph.OverlayNode /** Represents the contents of `sys.version_info` which contains the Python version. */ data class VersionInfo(var major: Long? = null, var minor: Long? = null, var micro: Long? = null) : - OverlayNode() { + OverlayNode(), Comparable { /** * Returns the version info as a tuple (major, minor, micro). The length of the tuple depends on * the information set, e.g., if only major version is set, then the list is 1 element long. @@ -46,6 +46,24 @@ data class VersionInfo(var major: Long? = null, var minor: Long? = null, var mic return list } + + override fun compareTo(other: VersionInfo): Int { + val thisMajor = this.major ?: -1 + val otherMajor = other.major ?: -1 + val thisMinor = this.minor ?: -1 + val otherMinor = other.minor ?: -1 + val thisMicro = this.micro ?: -1 + val otherMicro = other.micro ?: -1 + return if (thisMajor == otherMajor && thisMinor == otherMinor && thisMicro == otherMicro) { + 0 + } else if ( + thisMajor < otherMajor || + (thisMajor == otherMajor && + (thisMinor < otherMinor || thisMinor == otherMinor && thisMicro < otherMicro)) + ) { + -1 + } else 1 + } } /** diff --git a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/SystemInformationTest.kt b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/SystemInformationTest.kt new file mode 100644 index 00000000000..87e9c681cb8 --- /dev/null +++ b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/SystemInformationTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.frontends.python + +import kotlin.test.Test +import kotlin.test.assertTrue + +class SystemInformationTest { + @Test + fun testVersionInfo() { + val empty = VersionInfo() + val v123_2 = VersionInfo(1, 2, 3) + val v123 = VersionInfo(1, 2, 3) + val v321 = VersionInfo(3, 2, 1) + val v121 = VersionInfo(1, 2, 1) + val v132 = VersionInfo(1, 3, 2) + val v124 = VersionInfo(1, 2, 4) + assertTrue(empty < v123) + assertTrue(v123_2.compareTo(v123) == 0) + assertTrue(v123 > empty) + assertTrue(v123 < v321) + assertTrue(v121 < v123) + assertTrue(v123 < v132) + assertTrue(v123 < v124) + + assertTrue(v321 > v123) + assertTrue(v123 > v121) + assertTrue(v132 > v123) + assertTrue(v124 > v123) + } +}