From 954397f69c6661283c1821a1970b74692af56751 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Sch=C3=A4fer?= <david.schaefer@ufz.de>
Date: Mon, 15 Apr 2024 14:15:56 +0200
Subject: [PATCH] Support python 3.12

---
 .github/workflows/main.yml |  2 +-
 .gitlab-ci.yml             | 25 ++++++++++++++++++++++++-
 requirements.txt           |  2 +-
 saqc/funcs/tools.py        | 10 +++++-----
 saqc/parsing/visitor.py    |  9 ++-------
 setup.py                   |  2 +-
 6 files changed, 34 insertions(+), 16 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a919acfa6..a14efd71e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -29,7 +29,7 @@ jobs:
       fail-fast: false
       matrix:
         os: ["windows-latest", "ubuntu-latest", "macos-latest"]
-        python-version: ["3.9", "3.10", "3.11"]
+        python-version: ["3.9", "3.10", "3.11", "3.12"]
     defaults:
       run:
         # somehow this also works for windows O.o ??
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 78bc10f9e..f367f212e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -30,7 +30,7 @@ stages:
   - deploy
 
 default:
-  image: python:3.10
+  image: python:3.11
   before_script:
     - pip install --upgrade pip
     - pip install -r requirements.txt
@@ -133,6 +133,19 @@ python311:
     reports:
       junit: report.xml
 
+python312:
+  stage: test
+  image: python:3.12
+  script:
+    - export DISPLAY=:99
+    - Xvfb :99 &
+    - pytest tests -Werror --junitxml=report.xml
+    - python -m saqc --config docs/resources/data/config.csv --data docs/resources/data/data.csv --outfile /tmp/test.csv
+  artifacts:
+    when: always
+    reports:
+      junit: report.xml
+
 doctest:
   stage: test
   script:
@@ -180,6 +193,16 @@ wheel311:
     - pip install .
     - python -c 'import saqc; print(f"{saqc.__version__=}")'
 
+wheel312:
+  stage: build
+  image: python:3.12
+  variables:
+    PYPI_PKG_NAME: "saqc-dev"
+  script:
+    - pip install wheel
+    - pip wheel .
+    - pip install .
+    - python -c 'import saqc; print(f"{saqc.__version__=}")'
 
 # ===========================================================
 # Extra Pipeline (run with a successful run of all other jobs on develop)
diff --git a/requirements.txt b/requirements.txt
index 778157693..f44b07ae7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,7 @@
 
 Click==8.1.7
 docstring_parser==0.16
+fancy-collections==0.2.1
 fastdtw==0.3.4
 matplotlib==3.8.3
 numpy==1.26.4
@@ -13,4 +14,3 @@ pandas==2.2.1
 scikit-learn==1.4.1.post1
 scipy==1.12.0
 typing_extensions==4.10.0
-fancy-collections==0.2.1
diff --git a/saqc/funcs/tools.py b/saqc/funcs/tools.py
index 7348b9b78..453d28d88 100644
--- a/saqc/funcs/tools.py
+++ b/saqc/funcs/tools.py
@@ -10,7 +10,7 @@ from __future__ import annotations
 import pickle
 import tkinter as tk
 import warnings
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING
 
 import matplotlib as mpl
 import matplotlib.pyplot as plt
@@ -24,7 +24,7 @@ from saqc.lib.checking import validateChoice
 from saqc.lib.docs import DOC_TEMPLATES
 from saqc.lib.plotting import makeFig
 from saqc.lib.selectionGUI import MplScroller, SelectionOverlay
-from saqc.lib.tools import periodicMask, toSequence
+from saqc.lib.tools import periodicMask
 
 if TYPE_CHECKING:
     from saqc import SaQC
@@ -138,7 +138,7 @@ class ToolsMixin:
             if not _TEST_MODE:
                 root.destroy()
         else:  # show figure if only overlay is used
-            plt.show(block=~_TEST_MODE)
+            plt.show(block=not _TEST_MODE)
             plt.rcParams["toolbar"] = "toolbar2"
 
         # disconnect mouse events when GUI is closed
@@ -329,7 +329,7 @@ class ToolsMixin:
         datcol_idx = self._data[field].index
 
         if mode == "periodic":
-            mask = periodicMask(datcol_idx, start, end, ~closed)
+            mask = periodicMask(datcol_idx, start, end, closed)
         elif mode == "selection_field":
             idx = self._data[selection_field].index.intersection(datcol_idx)
             mask = self._data[selection_field].loc[idx]
@@ -357,7 +357,7 @@ class ToolsMixin:
         mode: Literal["subplots", "oneplot"] | str = "oneplot",
         history: Literal["valid", "complete"] | list[str] | None = "valid",
         xscope: slice | str | None = None,
-        yscope: tuple | list[tuple] | dict = None,
+        yscope: tuple | list[tuple] | dict | None = None,
         store_kwargs: dict | None = None,
         ax: mpl.axes.Axes | None = None,
         ax_kwargs: dict | None = None,
diff --git a/saqc/parsing/visitor.py b/saqc/parsing/visitor.py
index 7224e85a0..82c8abec2 100644
--- a/saqc/parsing/visitor.py
+++ b/saqc/parsing/visitor.py
@@ -9,8 +9,6 @@
 import ast
 import importlib
 
-import numpy as np
-
 from saqc.core.register import FUNC_MAP
 from saqc.parsing.environ import ENVIRONMENT
 
@@ -28,13 +26,12 @@ class ConfigExpressionParser(ast.NodeVisitor):
     """
 
     SUPPORTED = (
-        ast.Str,
+        ast.Constant,
         ast.Expression,
         ast.UnaryOp,
         ast.BinOp,
         ast.BitOr,
         ast.BitAnd,
-        ast.Num,
         ast.Compare,
         ast.Add,
         ast.Sub,
@@ -86,10 +83,8 @@ class ConfigExpressionParser(ast.NodeVisitor):
 class ConfigFunctionParser(ast.NodeVisitor):
     SUPPORTED_NODES = (
         ast.Call,
-        ast.Num,
-        ast.Str,
+        ast.Constant,
         ast.keyword,
-        ast.NameConstant,
         ast.UnaryOp,
         ast.Name,
         ast.Load,
diff --git a/setup.py b/setup.py
index 92ad10d48..0c5da8714 100644
--- a/setup.py
+++ b/setup.py
@@ -50,9 +50,9 @@ setup(
     python_requires=">=3.9",
     install_requires=[
         "Click",
-        "dtw",
         "docstring_parser",
         "fancy-collections",
+        "fastdtw",
         "matplotlib>=3.4",
         "numpy",
         "outlier-utils",
-- 
GitLab