From c7824eb76373a5c86ff80281120b98994382a5d9 Mon Sep 17 00:00:00 2001 From: Muhammad Yasirroni Date: Mon, 29 Dec 2025 18:42:35 +1100 Subject: [PATCH 1/4] add_read_csv --- .gitignore | 3 + .pre-commit-config.yaml | 7 +- matpowercaseframes/core.py | 129 ++++++++++---- notebooks/load_excel.ipynb | 93 +++++----- requirements-dev.txt | 18 +- tests/data/case118_test_to_xlsx.xlsx | Bin 33196 -> 0 bytes .../case118_test_to_xlsx_prefix_suffix.xlsx | Bin 33202 -> 0 bytes tests/data/case9_test_to_xlsx.xlsx | Bin 8645 -> 0 bytes .../case9_test_to_xlsx_prefix_suffix.xlsx | Bin 8654 -> 0 bytes tests/test_core.py | 164 ++++++++++++------ 10 files changed, 278 insertions(+), 136 deletions(-) delete mode 100644 tests/data/case118_test_to_xlsx.xlsx delete mode 100644 tests/data/case118_test_to_xlsx_prefix_suffix.xlsx delete mode 100644 tests/data/case9_test_to_xlsx.xlsx delete mode 100644 tests/data/case9_test_to_xlsx_prefix_suffix.xlsx diff --git a/.gitignore b/.gitignore index baeba57..60f82c6 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,6 @@ dmypy.json # Mac .DS_Store + +# test +tests/data/*.xlsx diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c33521c..412bb7e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,9 @@ -default_stages: [commit] +default_stages: [pre-commit] repos: # check yaml and end of file fixer - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v6.0.0 hooks: - id: check-yaml - id: end-of-file-fixer @@ -12,7 +12,7 @@ repos: # autofix using ruff - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.0 + rev: v0.14.10 hooks: # Run the linter. - id: ruff @@ -21,4 +21,3 @@ repos: # Run the formatter. - id: ruff-format types_or: [ python, pyi, jupyter ] - # args: [ --verbose ] diff --git a/matpowercaseframes/core.py b/matpowercaseframes/core.py index a2e6c3a..c4f7ddf 100644 --- a/matpowercaseframes/core.py +++ b/matpowercaseframes/core.py @@ -78,37 +78,46 @@ def _read_data( # TODO: support Path # TYPE: str of path path = self._get_path(data) - path_no_ext, ext = os.path.splitext(path) - if ext == ".m": - # read `.m` file - if load_case_engine is None: - # read with matpower parser - self._read_matpower( - filepath=path, - allow_any_keys=allow_any_keys, - ) - else: - # read using loadcase - mpc = load_case_engine.loadcase(path) - self._read_oct2py_struct( - struct=mpc, - allow_any_keys=allow_any_keys, - ) - elif ext == ".xlsx": - # read `.xlsx` file - self._read_excel( - filepath=path, + # check if path is a directory (for CSV files) + if os.path.isdir(path): + self._read_csv_dir( + dirpath=path, prefix=prefix, suffix=suffix, allow_any_keys=allow_any_keys, ) - self.name = os.path.basename(path_no_ext) + self.name = os.path.basename(path) else: - # TODO: support read directory of csv for schema and .csv data - message = f"Can't find data at {data}" - raise FileNotFoundError(message) - + path_no_ext, ext = os.path.splitext(path) + + if ext == ".m": + # read `.m` file + if load_case_engine is None: + # read with matpower parser + self._read_matpower( + filepath=path, + allow_any_keys=allow_any_keys, + ) + else: + # read using loadcase + mpc = load_case_engine.loadcase(path) + self._read_oct2py_struct( + struct=mpc, + allow_any_keys=allow_any_keys, + ) + elif ext == ".xlsx": + # read `.xlsx` file + self._read_excel( + filepath=path, + prefix=prefix, + suffix=suffix, + allow_any_keys=allow_any_keys, + ) + self.name = os.path.basename(path_no_ext) + else: + message = f"Can't find data at {os.path.abspath(data)}" + raise FileNotFoundError(message) elif isinstance(data, dict): # TYPE: dict | oct2py.io.Struct self._read_oct2py_struct( @@ -162,15 +171,17 @@ def _get_path(path): Determine the correct file path for the given input. Args: - path (str): File path or MATPOWER case name. + path (str): File path, directory path, or MATPOWER case name. Returns: - str: Resolved file path. + str: Resolved file path or directory path. Raises: FileNotFoundError: If the file or MATPOWER case cannot be found. """ - # TODO: support read directory of csv for schema and .csv data + # directory exist on path (for CSV directory) + if os.path.isdir(path): + return path # file exist on path if os.path.isfile(path): @@ -198,7 +209,9 @@ def _get_path(path): if os.path.isfile(path_added_matpower_m): return path_added_matpower_m - raise FileNotFoundError + # Create detailed error message + error_msg = f"Could not find file or directory '{path}'." + raise FileNotFoundError(error_msg) def _read_matpower(self, filepath, allow_any_keys=False): """ @@ -333,6 +346,64 @@ def _read_excel(self, filepath, prefix="", suffix="", allow_any_keys=False): self.setattr(attribute, value) + def _read_csv_dir(self, dirpath, prefix="", suffix="", allow_any_keys=False): + """ + Read data from a directory of CSV files. + + Args: + dirpath (str): Directory path containing the CSV files. + prefix (str): File prefix for each attribute CSV file. + suffix (str): File suffix for each attribute CSV file. + allow_any_keys (bool): Whether to allow any keys beyond ATTRIBUTES. + """ + # create a dictionary mapping attribute names to file paths + csv_data = {} + for csv_file in os.listdir(dirpath): + if csv_file.endswith(".csv"): + # remove prefix and suffix to get the attribute name + attribute = csv_file[:-4] # remove '.csv' extension + + if prefix and attribute.startswith(prefix): + attribute = attribute[len(prefix) :] + if suffix and attribute.endswith(suffix): + attribute = attribute[: -len(suffix)] + + csv_data[attribute] = os.path.join(dirpath, csv_file) + + self._attributes = [] + + # info CSV to extract general metadata + info_name = "info" + if info_name in csv_data: + info_data = pd.read_csv(csv_data[info_name], index_col=0) + + value = info_data.loc["version", "INFO"].item() + self.setattr("version", str(value)) + + value = info_data.loc["baseMVA", "INFO"].item() + self.setattr("baseMVA", value) + + # iterate through the remaining CSV files + for attribute, filepath in csv_data.items(): + # skip the info file + if attribute == info_name: + continue + + # check attribute rule + if attribute not in ATTRIBUTES and not allow_any_keys: + continue + + # read CSV file + sheet_data = pd.read_csv(filepath, index_col=0) + + if attribute in ["bus_name", "branch_name", "gen_name"]: + # convert back to an index + value = pd.Index(sheet_data[attribute].values.tolist(), name=attribute) + else: + value = sheet_data + + self.setattr(attribute, value) + def _get_dataframe(self, attribute, data, n_cols=None, columns_template=None): """ Create a DataFrame with proper columns from raw data. diff --git a/notebooks/load_excel.ipynb b/notebooks/load_excel.ipynb index 9ee1245..70a89c4 100644 --- a/notebooks/load_excel.ipynb +++ b/notebooks/load_excel.ipynb @@ -42,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -53,7 +53,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -69,7 +69,7 @@ " dtype='object', name='bus_name', length=118)" ] }, - "execution_count": 5, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -81,6 +81,30 @@ "# pd.Index(sheet_data[attribute].values.tolist(), name=attribute)" ] }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "path = \"../tests/data/case118_test_to_xlsx.xlsx\"\n", + "prefix = \"\"\n", + "suffix = \"\"\n", + "cf_org.to_excel(path, prefix=prefix, suffix=suffix) # write to .xlsx file" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "path = \"../tests/data/case118_test_to_xlsx_prefix_suffix.xlsx\"\n", + "prefix = \"mpc.\"\n", + "suffix = \"_test\"\n", + "cf_org.to_excel(path, prefix=prefix, suffix=suffix) # write to .xlsx file" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -97,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -153,7 +177,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -400,7 +424,7 @@ " dtype='object', name='bus_name', length=118)}" ] }, - "execution_count": 7, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -412,7 +436,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -421,7 +445,7 @@ "dict_keys(['info', 'bus', 'gen', 'branch', 'gencost', 'bus_name'])" ] }, - "execution_count": 8, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -439,7 +463,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -448,7 +472,7 @@ "['version', 'baseMVA', 'bus', 'gen', 'branch', 'gencost', 'bus_name']" ] }, - "execution_count": 9, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -467,7 +491,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -475,13 +499,13 @@ "output_type": "stream", "text": [ "\n", - "MATPOWER Version 8.0, 17-May-2024\n", + "MATPOWER Version 8.1, 12-Jul-2025\n", "Power Flow -- AC-polar-power formulation\n", "\n", "Newton's method converged in 3 iterations.\n", "PF successful\n", "\n", - "Converged in 0.13 seconds\n", + "Converged in 0.06 seconds\n", "================================================================================\n", "| System Summary |\n", "================================================================================\n", @@ -851,7 +875,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -907,7 +931,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -1154,7 +1178,7 @@ " dtype='object', name='bus_name', length=118)}" ] }, - "execution_count": 12, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -1166,7 +1190,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -1175,7 +1199,7 @@ "dict_keys(['mpc.info_test', 'mpc.bus_test', 'mpc.gen_test', 'mpc.branch_test', 'mpc.gencost_test', 'mpc.bus_name_test'])" ] }, - "execution_count": 13, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -1193,28 +1217,7 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['version', 'baseMVA', 'bus', 'gen', 'branch', 'gencost', 'bus_name']" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cf = CaseFrames(path, prefix=prefix, suffix=suffix)\n", - "cf.attributes" - ] - }, - { - "cell_type": "code", - "execution_count": 15, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -3943,7 +3946,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -3951,13 +3954,13 @@ "output_type": "stream", "text": [ "\n", - "MATPOWER Version 8.0, 17-May-2024\n", + "MATPOWER Version 8.1, 12-Jul-2025\n", "Power Flow -- AC-polar-power formulation\n", "\n", "Newton's method converged in 3 iterations.\n", "PF successful\n", "\n", - "Converged in 0.09 seconds\n", + "Converged in 0.04 seconds\n", "================================================================================\n", "| System Summary |\n", "================================================================================\n", @@ -4320,7 +4323,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -4351,7 +4354,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.8" + "version": "3.12.12" } }, "nbformat": 4, diff --git a/requirements-dev.txt b/requirements-dev.txt index 8df6db0..4513486 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,15 +1,15 @@ -pandas==2.2.2 -numpy==2.1.1 +pandas==2.3.3 +numpy==2.4.0 openpyxl==3.1.5 -oct2py==5.7.2 -matpower==8.0.0.2.1.8 +oct2py==5.8.0 +matpower==8.1.0.2.2.3 pre-commit==3.8.0 -ruff==0.6.4 -setuptools==74.1.2 +ruff==0.14.10 +setuptools==80.9.0 -pytest==8.3.2 -pytest-cov==5.0.0 -pytest-xdist==3.6.1 +pytest==9.0.2 +pytest-cov==7.0.0 +pytest-xdist==3.8.0 diff --git a/tests/data/case118_test_to_xlsx.xlsx b/tests/data/case118_test_to_xlsx.xlsx deleted file mode 100644 index 227535c992b1fa29e4f9cbdaee69066d64c13569..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33196 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%0AQhm`NOlwhf`=aU==EFsTl^B4_D#3Kl|cqB6lbr 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 diff --git a/tests/data/case118_test_to_xlsx_prefix_suffix.xlsx b/tests/data/case118_test_to_xlsx_prefix_suffix.xlsx deleted file mode 100644 index f7bf16f6d8e0a87855e08e66cee95c439c2386b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33202 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

-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 dwQ->|W&V?x>&ekxpKJTi!HAg#GH?k-PNH0y7 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 amjI-qh;)Bo`nAo)0%+cArViDwr~e0M$}cDY diff --git a/tests/data/case9_test_to_xlsx_prefix_suffix.xlsx b/tests/data/case9_test_to_xlsx_prefix_suffix.xlsx deleted file mode 100644 index f3490d6e118a1966043bf58814558e3d32a04446..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8654 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 diff --git a/tests/test_core.py b/tests/test_core.py index f6472e0..8fc2116 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -15,32 +15,31 @@ CURDIR = os.path.realpath(os.path.dirname(__file__)) CASE_DIR = os.path.join(os.path.dirname(CURDIR), "data") CASE_PATH_CASE9 = os.path.join(CASE_DIR, CASE_NAME_CASE9) +ATTRIBUTES_CASE9 = ["version", "baseMVA", "bus", "gen", "branch", "gencost"] CASE_NAME_CASE118 = "case118.m" CURDIR = os.path.realpath(os.path.dirname(__file__)) CASE_DIR = os.path.join(os.path.dirname(CURDIR), "data") CASE_PATH_CASE118 = os.path.join(CASE_DIR, CASE_NAME_CASE118) +ATTRIBUTES_CASE118 = [ + "version", + "baseMVA", + "bus", + "gen", + "branch", + "gencost", + "bus_name", +] +ATTRIBUTES_CASE = { + "case9": ATTRIBUTES_CASE9, + "case118": ATTRIBUTES_CASE118, +} def test_input_str_path(): CaseFrames(CASE_PATH_CASE9) -def test_read_excel(): - CASE_NAME = "tests/data/case118_test_to_xlsx.xlsx" - cf = CaseFrames(CASE_NAME) - for attribute in [ - "version", - "baseMVA", - "bus", - "gen", - "branch", - "gencost", - "bus_name", - ]: - assert attribute in cf.attributes - - def test_input_oct2py_io_Struct(): from matpower import start_instance @@ -149,40 +148,107 @@ def test_get_attributes(): # pytest -n auto --durations=0 -def test_to_xlsx(): - cf = CaseFrames(CASE_PATH_CASE9) - cf.to_excel("tests/results/case9/case9_test_to_xlsx.xlsx") - cf.to_excel( - "tests/results/case9_prefix_suffix/case9_test_to_xlsx_prefix_suffix.xlsx", - prefix="mpc.", - suffix="_test", - ) - - cf = CaseFrames(CASE_PATH_CASE118) - cf.to_excel("tests/results/case118/case118_test_to_xlsx.xlsx") - cf.to_excel( - "tests/results/case118_prefix_suffix/case118_test_to_xlsx_prefix_suffix.xlsx", - prefix="mpc.", - suffix="_test", - ) - - -def test_to_csv(): - cf = CaseFrames(CASE_PATH_CASE9) - cf.to_csv("tests/results/case9") - cf.to_csv("tests/results/case9_prefix_suffix", prefix="mpc.", suffix="_test") - - cf = CaseFrames(CASE_PATH_CASE118) - cf.to_csv("tests/results/case118") - cf.to_csv("tests/results/case118_prefix_suffix", prefix="mpc.", suffix="_test") - - -def test_to_schema(): - cf = CaseFrames(CASE_PATH_CASE9) - cf.to_schema("tests/results/case9/schema") - - cf = CaseFrames(CASE_PATH_CASE118) - cf.to_schema("tests/results/case118/schema") +@pytest.mark.parametrize( + "case_path,attributes,output_path,prefix,suffix", + [ + ( + CASE_PATH_CASE9, + ATTRIBUTES_CASE9, + "tests/results/case9/case9_test_to_xlsx.xlsx", + "", + "", + ), + ( + CASE_PATH_CASE9, + ATTRIBUTES_CASE9, + "tests/results/case9_prefix_suffix/case9_test_to_xlsx_prefix_suffix.xlsx", + "mpc.", + "_test", + ), + ( + CASE_PATH_CASE118, + ATTRIBUTES_CASE118, + "tests/results/case118/case118_test_to_xlsx.xlsx", + "", + "", + ), + ( + CASE_PATH_CASE118, + ATTRIBUTES_CASE118, + "tests/results/case118_prefix_suffix/case118_test_to_xlsx_prefix_suffix.xlsx", + "mpc.", + "_test", + ), + ], + ids=["case9", "case9_prefix_suffix", "case118", "case118_prefix_suffix"], +) +def test_to_and_read_xlsx(case_path, attributes, output_path, prefix, suffix): + cf = CaseFrames(case_path) # read .m file + cf.to_excel(output_path, prefix=prefix, suffix=suffix) # write to .xlsx file + cf = CaseFrames( + output_path, prefix=prefix, suffix=suffix + ) # read back from .xlsx file + for attribute in attributes: + assert attribute in cf.attributes, ( + f"Missing attribute '{attribute}' in {cf.attributes}" + ) + + +@pytest.mark.parametrize( + "case_path,attributes,output_dir,prefix,suffix", + [ + (CASE_PATH_CASE9, ATTRIBUTES_CASE9, "tests/results/case9", "", ""), + ( + CASE_PATH_CASE9, + ATTRIBUTES_CASE9, + "tests/results/case9_prefix_suffix", + "mpc.", + "_test", + ), + (CASE_PATH_CASE118, ATTRIBUTES_CASE118, "tests/results/case118", "", ""), + ( + CASE_PATH_CASE118, + ATTRIBUTES_CASE118, + "tests/results/case118_prefix_suffix", + "mpc.", + "_test", + ), + ], + ids=["case9", "case9_prefix_suffix", "case118", "case118_prefix_suffix"], +) +def test_to_and_read_csv(case_path, attributes, output_dir, prefix, suffix): + cf = CaseFrames(case_path) # read .m file + cf.to_csv(output_dir, prefix=prefix, suffix=suffix) # write to .csv directory + cf = CaseFrames( + output_dir, prefix=prefix, suffix=suffix + ) # read back from .csv directory + for attribute in attributes: + assert attribute in cf.attributes, ( + f"Missing attribute '{attribute}' in {cf.attributes}" + ) + + +@pytest.mark.parametrize( + "case_path,schema_dir,case_name", + [ + (CASE_PATH_CASE9, "tests/results/case9/schema", "case9"), + (CASE_PATH_CASE118, "tests/results/case118/schema", "case118"), + ], + ids=["case9", "case118"], +) +def test_to_schema(case_path, schema_dir, case_name): + cf = CaseFrames(case_path) + cf.to_schema(schema_dir) + assert os.path.isdir(schema_dir), f"Schema directory '{schema_dir}' was not created" + + schema_files = os.listdir(schema_dir) + assert len(schema_files) > 0, f"No schema files found in '{schema_dir}'" + + cf = CaseFrames(schema_dir) + for attribute in ATTRIBUTES_CASE[case_name]: + assert attribute in cf.attributes, ( + f"Missing attribute '{attribute}' in {cf.attributes}" + ) def test_to_dict(): From 5c21de091b0b0cbdef11ef63ee0ae5f536c55707 Mon Sep 17 00:00:00 2001 From: Muhammad Yasirroni Date: Mon, 29 Dec 2025 18:51:18 +1100 Subject: [PATCH 2/4] support python 3.14 --- .github/workflows/build.yml | 4 ++-- CONTRIBUTING.md | 17 ++++++++++++++++- pyproject.toml | 2 ++ requirements-dev.txt => requirements-all.txt | 0 4 files changed, 20 insertions(+), 3 deletions(-) rename requirements-dev.txt => requirements-all.txt (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 274fbd3..1b21609 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: "3.12" + python-version: "3.14" cache: 'pip' cache-dependency-path: 'requirements-dev.txt' - name: Install Ruff @@ -26,7 +26,7 @@ jobs: build: strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] platform: [octave] os: [ubuntu-latest] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bb88acd..4c20be2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,19 @@ -# Contirbuting +# Contributing + +## env + +```sh +uv venv env --python 3.14 +source env/bin/activate +uv pip install pip +``` + +## packages + +```sh +pip install pru +pru -r requirements-all.txt +``` ## Install in development mode diff --git a/pyproject.toml b/pyproject.toml index 092a7c7..1cb39d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,8 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Mathematics", "License :: OSI Approved :: MIT License", diff --git a/requirements-dev.txt b/requirements-all.txt similarity index 100% rename from requirements-dev.txt rename to requirements-all.txt From f08f63bd8d20e14bd592ce0d42838aaf0cd1e079 Mon Sep 17 00:00:00 2001 From: Muhammad Yasirroni Date: Mon, 29 Dec 2025 18:55:30 +1100 Subject: [PATCH 3/4] remove 3.7 and 3.8 on github workflow --- .github/workflows/build.yml | 2 +- .github/workflows/publish.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b21609..7d135c8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: build: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] platform: [octave] os: [ubuntu-latest] diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 27c23df..a77f54b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,7 +7,7 @@ jobs: build: strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] platform: [octave] os: [ubuntu-latest] @@ -45,10 +45,10 @@ jobs: - name: Clone this repository uses: actions/checkout@v3 - - name: Set up Python 3.12 + - name: Set up Python 3.14 uses: actions/setup-python@v4 with: - python-version: 3.12 + python-version: 3.14 cache: 'pip' cache-dependency-path: 'requirements-dev.txt' From 5e248fa9a8317e75e12832a0d733ac56823047af Mon Sep 17 00:00:00 2001 From: Muhammad Yasirroni Date: Mon, 29 Dec 2025 18:58:47 +1100 Subject: [PATCH 4/4] change cache dependency path --- .github/workflows/build.yml | 4 ++-- .github/workflows/publish.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d135c8..788fa95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: with: python-version: "3.14" cache: 'pip' - cache-dependency-path: 'requirements-dev.txt' + cache-dependency-path: 'requirements-all.txt' - name: Install Ruff run: | python -m pip install --upgrade pip @@ -41,7 +41,7 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: 'pip' - cache-dependency-path: 'requirements-dev.txt' + cache-dependency-path: 'requirements-all.txt' - name: Install Octave (Linux) if: matrix.platform == 'octave' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a77f54b..339c39a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,7 +22,7 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: 'pip' - cache-dependency-path: 'requirements-dev.txt' + cache-dependency-path: 'requirements-all.txt' - name: Install Octave (Linux) if: matrix.platform == 'octave' @@ -50,7 +50,7 @@ jobs: with: python-version: 3.14 cache: 'pip' - cache-dependency-path: 'requirements-dev.txt' + cache-dependency-path: 'requirements-all.txt' - name: Install build dependencies run: |