From 998ebe4414b4ceff8fdbc180cf06f1b4b6bad933 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Tue, 18 Aug 2020 16:37:23 +0200 Subject: [PATCH] Check Python runtime version using tuple comparison The previous implementation used pkg_resources.parse_version to parse the kaitaistruct module's __version__ string. Unfortunately, importing pkg_resources is relatively slow, which had a noticeable performance impact on every ksc-generated parser. The new implementation uses the newly added kaitaistruct.API_VERSION attribute, which stores the runtime version as a tuple of ints. In this format the version information can be compared directly without having to parse it from a string first. This allows removing the import of pkg_resources, which fixes the performance problem. Part of the fix for kaitai-io/kaitai_struct#804. --- .../scala/io/kaitai/struct/format/KSVersion.scala | 9 +++++++++ .../kaitai/struct/languages/PythonCompiler.scala | 14 ++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/format/KSVersion.scala b/shared/src/main/scala/io/kaitai/struct/format/KSVersion.scala index 766bd891e..43e1bd369 100644 --- a/shared/src/main/scala/io/kaitai/struct/format/KSVersion.scala +++ b/shared/src/main/scala/io/kaitai/struct/format/KSVersion.scala @@ -53,6 +53,15 @@ case class KSVersion(nums: List[Int]) extends Ordered[KSVersion] { val v1 :: v2 :: v3 :: _ = nums ++ List.fill(3 - nums.size)(0) "%d.%03d_%03d".format(v1, v2, v3) } + + /** + * Dumps a version as a tuple of ints in Python syntax, + * that is 1.2.3 becomes "(1, 2, 3)". + * There are no limitations on the number of version components or the range + * of values for each component. + * @return version as a tuple of ints in Python syntax + */ + def toPythonTuple: String = nums.mkString("(", ", ", ")") } object KSVersion { diff --git a/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala index bf5a3caaa..10d3065ca 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala @@ -43,7 +43,6 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) outHeader.puts(s"# $headerComment") outHeader.puts - importList.add("from pkg_resources import parse_version") importList.add("import kaitaistruct") importList.add(s"from kaitaistruct import $kstructName, $kstreamName, BytesIO") @@ -52,9 +51,16 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) // API compatibility check out.puts( - "if parse_version(kaitaistruct.__version__) < parse_version('" + - KSVersion.minimalRuntime + - "'):" + // The API_VERSION tuple attribute was introduced in version 0.9 of the + // Python runtime. Runtime version 0.8 and older only have a __version__ + // attribute that stores the version in string form. + // We don't need to include any complex handling for runtimes that don't + // have the API_VERSION attribute - we know that such a runtime must have + // version 0.8 or older, which means that it is incompatible with code + // generated by newer compiler versions. + "if getattr(kaitaistruct, 'API_VERSION', (0, 8)) < " + + KSVersion.minimalRuntime.toPythonTuple + + ":" ) out.inc out.puts(