diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 0271615..5edf449 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -13,25 +13,25 @@ jobs: os: - Ubuntu - Windows - - MacOs + - macOS py: + - "3.12" - "3.11" - "3.10" - "3.9" - - "3.8" steps: - name: Setup python for test ${{ matrix.py }} uses: actions/setup-python@v3 with: python-version: ${{ matrix.py }} - uses: actions/checkout@v3 - - name: Install soundlibs Ubuntu + - name: Install soundlibs on Ubuntu run: sudo apt-get update && sudo apt-get install --no-install-recommends -y --fix-missing pkg-config libsndfile1 if: matrix.os == 'Ubuntu' - - name: Install soundlibs MacOs + - name: Install soundlibs on macOS run: brew install libsndfile - if: matrix.os == 'MacOs' - - name: Install soundlibs Windows + if: matrix.os == 'macOS' + - name: Install soundlibs on Windows run: choco install libsndfile if: matrix.os == 'Windows' - name: Install tox @@ -39,6 +39,6 @@ jobs: # We will only check this on the minimum python version - name: Check formatting, lint and mypy run: tox -c tox.ini -e check-formatting,lint,mypy - if: matrix.py == '3.8' + if: matrix.py == '3.9' - name: Run test suite run: tox -c tox.ini -e py,manifest diff --git a/realbook/layers/compatibility.py b/realbook/layers/compatibility.py index 836fdb7..c1114d6 100644 --- a/realbook/layers/compatibility.py +++ b/realbook/layers/compatibility.py @@ -16,18 +16,21 @@ # limitations under the License. import os +from typing import Any, Dict, List, Optional, Tuple, Union, cast + import tensorflow as tf from google.protobuf.json_format import ( MessageToDict as SerializeProtobufToDict, +) +from google.protobuf.json_format import ( ParseDict as ParseDictToProtobuf, ) -from typing import Any, cast, Dict, List, Optional, Tuple, Union +from tensorflow.python.eager.wrap_function import WrappedFunction from tensorflow.python.framework.convert_to_constants import ( convert_variables_to_constants_v2_as_graph, ) from tensorflow.python.framework.importer import _IsControlInput as is_control_input from tensorflow.python.ops.op_selector import UnliftableError -from tensorflow.python.eager.wrap_function import WrappedFunction def _load_concrete_function_from_graph_def( @@ -132,7 +135,9 @@ def call(self, _input: Union[tf.Tensor, Dict[str, tf.Tensor]]) -> Union[tf.Tenso return self.model(_input) -def get_saved_model_input_tensors(saved_model_or_path: Union[tf.keras.Model, str]) -> List[tf.Tensor]: +def get_saved_model_input_tensors( + saved_model_or_path: Union[tf.keras.Model, str], +) -> List[tf.Tensor]: """ Given a path to a SavedModel or an already loaded SavedModel, return a list of its input tensors. Useful for figuring out the @@ -150,7 +155,9 @@ def get_saved_model_input_tensors(saved_model_or_path: Union[tf.keras.Model, str return [tensor for tensor in model.inputs if tensor.dtype != "resource"] -def get_all_tensors_from_saved_model(saved_model_or_path: Union[tf.keras.Model, str]) -> List[tf.Tensor]: +def get_all_tensors_from_saved_model( + saved_model_or_path: Union[tf.keras.Model, str], +) -> List[tf.Tensor]: """ Given a path to a SavedModel or an already loaded SavedModel, return a list of all of its tensors. Useful for figuring out the @@ -208,7 +215,9 @@ def get_all_tensors_from_saved_model(saved_model_or_path: Union[tf.keras.Model, return res -def get_saved_model_output_tensors(saved_model_or_path: Union[tf.keras.Model, str]) -> List[tf.Tensor]: +def get_saved_model_output_tensors( + saved_model_or_path: Union[tf.keras.Model, str], +) -> List[tf.Tensor]: """ Given a path to a SavedModel or an already loaded SavedModel, return a list of its output tensors. Useful for figuring out the diff --git a/realbook/layers/math.py b/realbook/layers/math.py index b816b30..beaaaa7 100644 --- a/realbook/layers/math.py +++ b/realbook/layers/math.py @@ -42,7 +42,7 @@ class NormalizedLog(tf.keras.layers.Layer): def build(self, input_shape: tf.Tensor) -> None: self.squeeze_batch = lambda batch: batch - rank = input_shape.rank + rank = len(input_shape) if rank == 4: assert input_shape[1] == 1, "If the rank is 4, the second dimension must be length 1" self.squeeze_batch = lambda batch: tf.squeeze(batch, axis=1) diff --git a/realbook/layers/signal.py b/realbook/layers/signal.py index aa7d69f..1852a3a 100644 --- a/realbook/layers/signal.py +++ b/realbook/layers/signal.py @@ -18,8 +18,8 @@ import warnings from typing import Any, Callable, Dict, Optional, Union -import tensorflow as tf import numpy as np +import tensorflow as tf from realbook.layers.math import log_base_b from realbook.vendor import librosa_filters @@ -86,7 +86,7 @@ def __init__( self.pad_mode = pad_mode def build(self, input_shape: tf.TensorShape) -> None: - if input_shape.rank > 2: + if len(input_shape) > 2: raise ValueError( "realbook.layers.signal.Stft received an input shape of " f"{input_shape}, but only supports inputs shaped " @@ -104,7 +104,7 @@ def build(self, input_shape: tf.TensorShape) -> None: self.spec = tf.keras.layers.Lambda( lambda x: tf.pad( x, - [[0, 0] for _ in range(input_shape.rank - 1)] + [[self.fft_length // 2, self.fft_length // 2]], + [[0, 0] for _ in range(len(input_shape) - 1)] + [[self.fft_length // 2, self.fft_length // 2]], mode=self.pad_mode, ) ) @@ -198,7 +198,7 @@ def __init__( ) def build(self, input_shape: tf.TensorShape) -> None: - if input_shape.rank > 3 or input_shape.rank <= 1: + if len(input_shape) > 3 or len(input_shape) <= 1: raise ValueError( "realbook.layers.signal.Istft received an input shape of " f"{input_shape}, but only supports inputs shaped " @@ -211,7 +211,7 @@ def build(self, input_shape: tf.TensorShape) -> None: self.window_sum = librosa_filters.window_sumsquare( # type: ignore window=self.window.numpy(), - n_frames=input_shape[0] if input_shape.rank == 2 else input_shape[1], + n_frames=input_shape[0] if len(input_shape) == 2 else input_shape[1], win_length=self.window_length, n_fft=self.fft_length, hop_length=self.hop_length, @@ -229,7 +229,7 @@ def build(self, input_shape: tf.TensorShape) -> None: self.slice_op = tf.keras.layers.Lambda(lambda x: x) if self.center: - if input_shape.rank == 2: # unbatched + if len(input_shape) == 2: # unbatched self.slice_op = tf.keras.layers.Lambda( lambda x: x[int(self.fft_length // 2) : -int(self.fft_length // 2)] ) diff --git a/setup.cfg b/setup.cfg index af71c08..76989a8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.0.3 +current_version = 1.0.4 commit = True tag = True @@ -22,10 +22,10 @@ classifiers = Operating System :: MacOS :: MacOS X Operating System :: Microsoft :: Windows Programming Language :: Python - Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Programming Language :: Python :: Implementation :: CPython [options] @@ -35,9 +35,9 @@ include_package_data = True # Note we don't yet support TensorFlow 2.16 or later due to Keras 3, # which does not support complex datatypes. install_requires = - tensorflow>=2.4,<2.16; sys_platform != 'darwin' or platform.machine != 'arm64' + tensorflow>=2.4,<=2.18; sys_platform != 'darwin' or platform.machine != 'arm64' # TF 2.13 finally publishes ARM wheels: - tensorflow>=2.13,<2.16; sys_platform == 'darwin' and platform.machine == 'arm64' + tensorflow>=2.13,<=2.18; sys_platform == 'darwin' and platform.machine == 'arm64' types-protobuf numpy typing_extensions @@ -61,9 +61,9 @@ test = nnaudio numpy>=1.22 librosa>=0.9 - tensorflow>=2.4,<2.16; sys_platform != 'darwin' or platform.machine != 'arm64' + tensorflow>=2.4,<=2.18; sys_platform != 'darwin' or platform.machine != 'arm64' # TF 2.13 finally publishes ARM wheels: - tensorflow>=2.13,<2.16; sys_platform == 'darwin' and platform.machine == 'arm64' + tensorflow>=2.13,<=2.18; sys_platform == 'darwin' and platform.machine == 'arm64' [bumpversion:file:realbook/__init__.py] diff --git a/tests/layers/test_compatibility.py b/tests/layers/test_compatibility.py index 66a3f21..96995c3 100644 --- a/tests/layers/test_compatibility.py +++ b/tests/layers/test_compatibility.py @@ -17,24 +17,25 @@ import os import tempfile +from contextlib import contextmanager from typing import Iterator, List, Tuple -import tensorflow as tf -from tensorflow.keras.layers import Dense, Concatenate + import numpy as np import pytest -from contextlib import contextmanager -from tensorflow.python.framework.importer import _IsControlInput as is_control_input +import tensorflow as tf +from tensorflow.keras.layers import Concatenate, Dense +from tensorflow.lite.python.util import get_grappler_config, run_graph_optimizations from tensorflow.python.framework.convert_to_constants import ( convert_variables_to_constants_v2_as_graph, ) -from tensorflow.lite.python.util import run_graph_optimizations, get_grappler_config +from tensorflow.python.framework.importer import _IsControlInput as is_control_input from realbook.layers.compatibility import ( FrozenGraphLayer, SavedModelLayer, - get_all_tensors_from_saved_model, - create_function_from_tensors, TensorWrapperLayer, + create_function_from_tensors, + get_all_tensors_from_saved_model, ) NUM_INPUT_VALUES = 10 @@ -70,7 +71,7 @@ def train_addition_model() -> tf.keras.models.Model: # Learn a model that sums numbers: model = tf.keras.Sequential( [ - tf.keras.layers.InputLayer(NUM_INPUT_VALUES), + tf.keras.layers.InputLayer((NUM_INPUT_VALUES,)), tf.keras.layers.Dense(1, name="my_layer"), ] ) @@ -83,7 +84,7 @@ def train_addition_model() -> tf.keras.models.Model: def train_named_input_and_output() -> tf.keras.models.Model: # Learn a model that sums numbers: - inputs = {"named_input": tf.keras.layers.Input(NUM_INPUT_VALUES)} + inputs = {"named_input": tf.keras.layers.Input((NUM_INPUT_VALUES,))} outputs = {"named_output": tf.keras.layers.Dense(1)(inputs["named_input"])} model = tf.keras.models.Model(inputs=inputs, outputs=outputs) model.compile(loss="MSE") @@ -97,7 +98,7 @@ def train_multi_input_multi_output() -> tf.keras.models.Model: """Train a dummy model to add numbers, but with multiple inputs and outputs: outAB = inA + inB, etc.""" input_names = ["inA", "inB", "inC"] - inputs = {input_name: tf.keras.layers.Input(1) for input_name in input_names} + inputs = {input_name: tf.keras.layers.Input((1,)) for input_name in input_names} output_names = ["outAB", "outBC", "outAC"] concatenated = Concatenate()([v for _, v in sorted(inputs.items(), key=lambda t: t[0])]) @@ -177,7 +178,7 @@ def test_reserialize_model() -> None: from_frozen = tf.keras.Sequential( [ - tf.keras.layers.InputLayer(NUM_INPUT_VALUES), + tf.keras.layers.InputLayer((NUM_INPUT_VALUES,)), FrozenGraphLayer( f.name, input_tensor_names=[t.name for t in input_tensors], diff --git a/tests/layers/test_nnaudio.py b/tests/layers/test_nnaudio.py index 5331ef2..c18675e 100644 --- a/tests/layers/test_nnaudio.py +++ b/tests/layers/test_nnaudio.py @@ -15,16 +15,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import tensorflow as tf -import torch +import platform + import numpy as np import pytest -import platform +import tensorflow as tf +import torch try: import librosa - from realbook.layers import nnaudio as our_nnaudio from nnAudio.Spectrogram import CQT2010v2 + + from realbook.layers import nnaudio as our_nnaudio except ImportError as e: if "numpy.core.multiarray failed to import" in str(e) and platform.system() == "Windows": librosa = None @@ -35,7 +37,6 @@ from typing import Tuple, Union - TEST_SAMPLE_RATE = 22050 diff --git a/tests/layers/test_signal.py b/tests/layers/test_signal.py index c9c8419..357a7c8 100644 --- a/tests/layers/test_signal.py +++ b/tests/layers/test_signal.py @@ -15,9 +15,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Union, List - import platform +from typing import List, Optional, Union + import numpy as np import pytest import tensorflow as tf diff --git a/tox.ini b/tox.ini index 964fe40..650a3da 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37,py38,py39,py310,manifest,check-formatting,lint,mypy +envlist = py39,py310,py311,py312,manifest,check-formatting,lint,mypy skipsdist = True usedevelop = True @@ -18,27 +18,27 @@ skip_install = true commands = check-manifest --ignore 'tests/*' [testenv:check-formatting] -basepython = python3.8 +basepython = python3.9 deps = black skip_install = true commands = black {env:SOURCE} tests --line-length {env:LINE_LENGTH} --diff --check [testenv:format] -basepython = python3.8 +basepython = python3.9 deps = black skip_install = true commands = black {env:SOURCE} tests --line-length {env:LINE_LENGTH} [testenv:lint] -basepython = python3.8 +basepython = python3.9 deps = flake8 skip_install = true commands = flake8 [testenv:mypy] -basepython = python3.8 +basepython = python3.9 deps = mypy types-protobuf