diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 045d18f..2f3745c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,20 +5,30 @@ on: [push] jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.8', '3.11'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: - python-version: '3.x' + python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - pip install PyYAML - pip install websocket_client - pip install . + pip install ".[dev]" - name: Test with pytest run: | pip install pytest pip install pytest-cov pytest --cov=camilladsp --cov-report=html + - name: pylint + run: | + pylint camilladsp + - name: Lint types + run: | + mypy --install-types --non-interactive camilladsp + - name: Lint formatting + run: | + black . --check --verbose \ No newline at end of file diff --git a/camilladsp/camilladsp.py b/camilladsp/camilladsp.py index b5467b3..b6c61e0 100644 --- a/camilladsp/camilladsp.py +++ b/camilladsp/camilladsp.py @@ -29,6 +29,7 @@ VERSION = "2.0.0-alpha2" + class CamillaError(ValueError): """ A class representing errors returned by CamillaDSP. @@ -858,8 +859,8 @@ def library(self) -> Tuple[str, str, str]: Tuple[List[str], List[str]] | None: A tuple containing the pyCamillaDSP version, as (major, minor, patch). """ - v = VERSION.split(".") - return (v[0], v[1], v[2]) + ver = VERSION.split(".") + return (ver[0], ver[1], ver[2]) class CamillaClient(_CamillaWS): diff --git a/examples/playwav/analyze_wav.py b/examples/playwav/analyze_wav.py index 38a3349..6f1846e 100644 --- a/examples/playwav/analyze_wav.py +++ b/examples/playwav/analyze_wav.py @@ -1,49 +1,51 @@ import struct import logging -sampleformats = {1: "int", +sampleformats = { + 1: "int", 3: "float", - } +} + def analyze_chunk(type, start, length, file, wav_info): if type == "fmt ": data = file.read(length) - wav_info['SampleFormat'] = sampleformats[struct.unpack('= input_filesize: break file_in.close() return wav_info - + + if __name__ == "__main__": import sys + info = read_wav_header(sys.argv[1]) print("Wav properties:") for name, val in info.items(): diff --git a/examples/playwav/play_wav.py b/examples/playwav/play_wav.py index 963aba9..544cb83 100644 --- a/examples/playwav/play_wav.py +++ b/examples/playwav/play_wav.py @@ -37,7 +37,7 @@ cfg["devices"]["capture_samplerate"] = wav_info["SampleRate"] cfg["devices"]["enable_rate_adjust"] = False if cfg["devices"]["samplerate"] != cfg["devices"]["capture_samplerate"]: - cfg["devices"]["resampler"] = { "type": "Synchronous" } + cfg["devices"]["resampler"] = {"type": "Synchronous"} else: cfg["devices"]["resampler"] = None cfg["devices"]["capture"] = capt_device diff --git a/examples/read_rms/read_rms.py b/examples/read_rms/read_rms.py index 7dbd721..1962234 100644 --- a/examples/read_rms/read_rms.py +++ b/examples/read_rms/read_rms.py @@ -19,4 +19,3 @@ while True: print(cdsp.levels.playback_rms()) time.sleep(1) - diff --git a/examples/set_volume/set_volume.py b/examples/set_volume/set_volume.py index 1263b7e..c9fbc44 100644 --- a/examples/set_volume/set_volume.py +++ b/examples/set_volume/set_volume.py @@ -7,7 +7,9 @@ port = int(sys.argv[1]) new_vol = float(sys.argv[2]) except: - print("Usage: Make sure that your pipeline includes Volume filters for each channel.") + print( + "Usage: Make sure that your pipeline includes Volume filters for each channel." + ) print("Then start CamillaDSP with the websocket server enabled:") print("> camilladsp -p4321 yourconfig.yml") print("Then set the volume") @@ -21,4 +23,3 @@ print(f"Current volume: {current_vol} dB") print(f"Changing volume to: {new_vol} dB") cdsp.volume.set_main(new_vol) - diff --git a/tests/test_camillaws.py b/tests/test_camillaws.py index 906e640..0b08dbb 100644 --- a/tests/test_camillaws.py +++ b/tests/test_camillaws.py @@ -4,6 +4,7 @@ import camilladsp import json + class DummyWS: def __init__(self): self.query = None @@ -13,17 +14,45 @@ def __init__(self): responses = { '"GetState"': json.dumps({"GetState": {"result": "Ok", "value": "Inactive"}}), '"GetVersion"': json.dumps({"GetVersion": {"result": "Ok", "value": "0.3.2"}}), - '"GetSupportedDeviceTypes"': json.dumps({"GetSupportedDeviceTypes": {"result": "Ok", "value": [["a", "b"], ["c", "d"]]}}), - '"GetSignalRange"': json.dumps({"GetSignalRange": {"result": "Ok", "value": "0.2"}}), - '"GetCaptureSignalRms"': json.dumps({"GetCaptureSignalRms": {"result": "Ok", "value": [0.1, 0.2]}}), - '"GetCaptureRate"': json.dumps({"GetCaptureRate": {"result": "Ok", "value": "88250"}}), - '{"GetFaderVolume": 1}': json.dumps({"GetFaderVolume": {"result": "Ok", "value": [1, -1.23]}}), - '"GetErrorValue"': json.dumps({"GetErrorValue": {"result": "Error", "value": "badstuff"}}), + '"GetSupportedDeviceTypes"': json.dumps( + { + "GetSupportedDeviceTypes": { + "result": "Ok", + "value": [["a", "b"], ["c", "d"]], + } + } + ), + '"GetSignalRange"': json.dumps( + {"GetSignalRange": {"result": "Ok", "value": "0.2"}} + ), + '"GetCaptureSignalRms"': json.dumps( + {"GetCaptureSignalRms": {"result": "Ok", "value": [0.1, 0.2]}} + ), + '"GetCaptureRate"': json.dumps( + {"GetCaptureRate": {"result": "Ok", "value": "88250"}} + ), + '{"GetFaderVolume": 1}': json.dumps( + {"GetFaderVolume": {"result": "Ok", "value": [1, -1.23]}} + ), + '"GetErrorValue"': json.dumps( + {"GetErrorValue": {"result": "Error", "value": "badstuff"}} + ), '"GetError"': json.dumps({"GetError": {"result": "Error"}}), '"Invalid"': json.dumps({"Invalid": {"result": "Error", "value": "badstuff"}}), - '"GetStopReason"': json.dumps({"GetStopReason": {"result": "Ok", "value": "Done"}}), - '"GetStopReason2"': json.dumps({"GetStopReason": {"result": "Ok", "value": {'CaptureFormatChange': 44098}}}), - '"GetStopReason3"': json.dumps({"GetStopReason": {"result": "Ok", "value": {'CaptureError': 'error error'}}}), + '"GetStopReason"': json.dumps( + {"GetStopReason": {"result": "Ok", "value": "Done"}} + ), + '"GetStopReason2"': json.dumps( + {"GetStopReason": {"result": "Ok", "value": {"CaptureFormatChange": 44098}}} + ), + '"GetStopReason3"': json.dumps( + { + "GetStopReason": { + "result": "Ok", + "value": {"CaptureError": "error error"}, + } + } + ), '"NotACommand"': json.dumps({"Invalid": {"result": "Error"}}), '{"SetSomeValue": 123}': json.dumps({"SetSomeValue": {"result": "Ok"}}), '"nonsense"': "abcdefgh", @@ -34,7 +63,7 @@ def send(self, query): if query == '"fail"': raise IOError("not connected") self.query = query - #if ":" in query: + # if ":" in query: # query, val = query.split(":",1) # self.response = "OK:{}".format(query.upper()) # self.value = val @@ -42,8 +71,9 @@ def send(self, query): if query in self.responses: self.response = self.responses[query] else: - self.response = json.dumps({"Invalid": {"result": "Error", "value": "badstuff"}}) - + self.response = json.dumps( + {"Invalid": {"result": "Error", "value": "badstuff"}} + ) def recv(self): print(self.response) @@ -57,89 +87,112 @@ def camilla_mockws(): ws_dummy = DummyWS() connection.send = MagicMock(side_effect=ws_dummy.send) connection.recv = MagicMock(side_effect=ws_dummy.recv) - with patch('camilladsp.camilladsp.create_connection', create_connection): + with patch("camilladsp.camilladsp.create_connection", create_connection): cdsp = camilladsp.camilladsp.CamillaClient("localhost", 1234) cdsp.dummyws = ws_dummy cdsp.mockconnection = connection yield cdsp + @pytest.fixture def camilla(): cdsp = camilladsp.camilladsp.CamillaClient("localhost", 12345) yield cdsp + @pytest.fixture def camilla_mockquery(): query_dummy = MagicMock() - with patch('camilladsp.camilladsp.CamillaClient.query', query_dummy): + with patch("camilladsp.camilladsp.CamillaClient.query", query_dummy): cdsp = camilladsp.camilladsp.CamillaClient("localhost", 1234) yield cdsp + @pytest.fixture def camilla_mockquery_yaml(): query_dummy = MagicMock(return_value="some: value\n") - with patch('camilladsp.camilladsp.CamillaClient.query', query_dummy): + with patch("camilladsp.camilladsp.CamillaClient.query", query_dummy): cdsp = camilladsp.camilladsp.CamillaClient("localhost", 1234) yield cdsp + def test_connect(camilla_mockws): with pytest.raises(IOError): camilla_mockws.general.state() camilla_mockws.connect() assert camilla_mockws.is_connected() assert camilla_mockws.general.state() == camilladsp.ProcessingState.INACTIVE - assert camilla_mockws.versions.camilladsp() == ('0', '3', '2') - assert camilla_mockws.versions.library() == tuple(camilladsp.camilladsp.VERSION.split(".")) + assert camilla_mockws.versions.camilladsp() == ("0", "3", "2") + assert camilla_mockws.versions.library() == tuple( + camilladsp.camilladsp.VERSION.split(".") + ) camilla_mockws.disconnect() assert not camilla_mockws.is_connected() + def test_connect_fail(camilla): with pytest.raises(IOError): camilla.connect() + def test_device_types(camilla_mockws): camilla_mockws.connect() assert camilla_mockws.general.supported_device_types() == (["a", "b"], ["c", "d"]) + def test_signal_range(camilla_mockws): camilla_mockws.connect() assert camilla_mockws.levels.range() == 0.2 + def test_signal_rms(camilla_mockws): camilla_mockws.connect() assert camilla_mockws.levels.capture_rms() == [0.1, 0.2] + def test_signal_range_decibel(camilla_mockws): camilla_mockws.connect() assert camilla_mockws.levels.range_decibel() == -20 - camilla_mockws.dummyws.responses['"GetSignalRange"'] = json.dumps({"GetSignalRange": {"result": "Ok", "value": "0.0"}}) + camilla_mockws.dummyws.responses['"GetSignalRange"'] = json.dumps( + {"GetSignalRange": {"result": "Ok", "value": "0.0"}} + ) assert camilla_mockws.levels.range_decibel() == -1000 + def test_disconnect_fail(camilla_mockws): camilla_mockws.connect() + def raise_error(): raise IOError("disconnected") + camilla_mockws.mockconnection.close = MagicMock(side_effect=raise_error) camilla_mockws.disconnect() assert not camilla_mockws.is_connected() + def test_capture_rate(camilla_mockws): camilla_mockws.connect() assert camilla_mockws.rate.capture() == 88200 assert camilla_mockws.rate.capture_raw() == 88250 + def test_stop_reason(camilla_mockws): camilla_mockws.connect() assert camilla_mockws.general.stop_reason() == StopReason.DONE assert camilla_mockws.general.stop_reason().data == None print(camilla_mockws.dummyws.responses) - camilla_mockws.dummyws.responses['"GetStopReason"'] = camilla_mockws.dummyws.responses['"GetStopReason2"'] + camilla_mockws.dummyws.responses[ + '"GetStopReason"' + ] = camilla_mockws.dummyws.responses['"GetStopReason2"'] assert camilla_mockws.general.stop_reason() == StopReason.CAPTUREFORMATCHANGE assert camilla_mockws.general.stop_reason().data == 44098 - camilla_mockws.dummyws.responses['"GetStopReason"'] = camilla_mockws.dummyws.responses['"GetStopReason3"'] + camilla_mockws.dummyws.responses[ + '"GetStopReason"' + ] = camilla_mockws.dummyws.responses['"GetStopReason3"'] assert camilla_mockws.general.stop_reason() == StopReason.CAPTUREERROR assert camilla_mockws.general.stop_reason().data == "error error" + def test_query(camilla_mockws): camilla_mockws.connect() with pytest.raises(camilladsp.CamillaError): @@ -149,12 +202,13 @@ def test_query(camilla_mockws): with pytest.raises(camilladsp.CamillaError): camilla_mockws.query("Invalid") with pytest.raises(IOError): - camilla_mockws.query("bug_in_ws") + camilla_mockws.query("bug_in_ws") with pytest.raises(IOError): camilla_mockws.query("NotACommand") with pytest.raises(IOError): camilla_mockws.query("fail") + def test_query_mockedws(camilla_mockws): camilla_mockws.connect() assert camilla_mockws.query("SetSomeValue", arg=123) is None @@ -162,69 +216,72 @@ def test_query_mockedws(camilla_mockws): assert camilla_mockws.general.supported_device_types() == (["a", "b"], ["c", "d"]) assert camilla_mockws.volume.fader(1) == -1.23 + def test_queries(camilla_mockquery): camilla_mockquery.rate.capture() - camilla_mockquery.query.assert_called_with('GetCaptureRate') + camilla_mockquery.query.assert_called_with("GetCaptureRate") camilla_mockquery.rate.capture_raw() - camilla_mockquery.query.assert_called_with('GetCaptureRate') + camilla_mockquery.query.assert_called_with("GetCaptureRate") camilla_mockquery.levels.range() - camilla_mockquery.query.assert_called_with('GetSignalRange') + camilla_mockquery.query.assert_called_with("GetSignalRange") camilla_mockquery.levels.range_decibel() - camilla_mockquery.query.assert_called_with('GetSignalRange') + camilla_mockquery.query.assert_called_with("GetSignalRange") camilla_mockquery.settings.set_update_interval(1234) - camilla_mockquery.query.assert_called_with('SetUpdateInterval', arg=1234) + camilla_mockquery.query.assert_called_with("SetUpdateInterval", arg=1234) camilla_mockquery.settings.update_interval() - camilla_mockquery.query.assert_called_with('GetUpdateInterval') + camilla_mockquery.query.assert_called_with("GetUpdateInterval") camilla_mockquery.general.stop() - camilla_mockquery.query.assert_called_with('Stop') + camilla_mockquery.query.assert_called_with("Stop") camilla_mockquery.general.exit() - camilla_mockquery.query.assert_called_with('Exit') + camilla_mockquery.query.assert_called_with("Exit") camilla_mockquery.general.reload() - camilla_mockquery.query.assert_called_with('Reload') + camilla_mockquery.query.assert_called_with("Reload") camilla_mockquery.config.file_path() - camilla_mockquery.query.assert_called_with('GetConfigFilePath') + camilla_mockquery.query.assert_called_with("GetConfigFilePath") camilla_mockquery.config.set_file_path("some/path") - camilla_mockquery.query.assert_called_with('SetConfigFilePath', arg="some/path") + camilla_mockquery.query.assert_called_with("SetConfigFilePath", arg="some/path") camilla_mockquery.config.active_raw() - camilla_mockquery.query.assert_called_with('GetConfig') + camilla_mockquery.query.assert_called_with("GetConfig") camilla_mockquery.config.set_active_raw("some:yaml") - camilla_mockquery.query.assert_called_with('SetConfig', arg="some:yaml") - camilla_mockquery.config.set_active({"some":"yaml"}) - camilla_mockquery.query.assert_called_with('SetConfig', arg='some: yaml\n') + camilla_mockquery.query.assert_called_with("SetConfig", arg="some:yaml") + camilla_mockquery.config.set_active({"some": "yaml"}) + camilla_mockquery.query.assert_called_with("SetConfig", arg="some: yaml\n") camilla_mockquery.status.rate_adjust() - camilla_mockquery.query.assert_called_with('GetRateAdjust') + camilla_mockquery.query.assert_called_with("GetRateAdjust") camilla_mockquery.status.buffer_level() - camilla_mockquery.query.assert_called_with('GetBufferLevel') + camilla_mockquery.query.assert_called_with("GetBufferLevel") camilla_mockquery.status.clipped_samples() - camilla_mockquery.query.assert_called_with('GetClippedSamples') + camilla_mockquery.query.assert_called_with("GetClippedSamples") camilla_mockquery.volume.main() - camilla_mockquery.query.assert_called_with('GetVolume') + camilla_mockquery.query.assert_called_with("GetVolume") camilla_mockquery.volume.set_main(-25.0) - camilla_mockquery.query.assert_called_with('SetVolume', arg=-25.0) + camilla_mockquery.query.assert_called_with("SetVolume", arg=-25.0) camilla_mockquery.mute.main() - camilla_mockquery.query.assert_called_with('GetMute') + camilla_mockquery.query.assert_called_with("GetMute") camilla_mockquery.mute.set_main(False) - camilla_mockquery.query.assert_called_with('SetMute', arg=False) + camilla_mockquery.query.assert_called_with("SetMute", arg=False) camilla_mockquery.levels.capture_rms() - camilla_mockquery.query.assert_called_with('GetCaptureSignalRms') + camilla_mockquery.query.assert_called_with("GetCaptureSignalRms") camilla_mockquery.levels.capture_peak() - camilla_mockquery.query.assert_called_with('GetCaptureSignalPeak') + camilla_mockquery.query.assert_called_with("GetCaptureSignalPeak") camilla_mockquery.levels.playback_rms() - camilla_mockquery.query.assert_called_with('GetPlaybackSignalRms') + camilla_mockquery.query.assert_called_with("GetPlaybackSignalRms") camilla_mockquery.levels.playback_peak() - camilla_mockquery.query.assert_called_with('GetPlaybackSignalPeak') + camilla_mockquery.query.assert_called_with("GetPlaybackSignalPeak") camilla_mockquery.volume.set_fader(1, -1.23) - camilla_mockquery.query.assert_called_with('SetFaderVolume', arg=(1, -1.23)) + camilla_mockquery.query.assert_called_with("SetFaderVolume", arg=(1, -1.23)) def test_queries_adv(camilla_mockquery_yaml): camilla_mockquery_yaml.config.read_and_parse_file("some/path") - camilla_mockquery_yaml.query.assert_called_with('ReadConfigFile', arg="some/path") + camilla_mockquery_yaml.query.assert_called_with("ReadConfigFile", arg="some/path") camilla_mockquery_yaml.config.parse_yaml("rawyaml") - camilla_mockquery_yaml.query.assert_called_with('ReadConfig', arg="rawyaml") - camilla_mockquery_yaml.config.validate({"some":"yaml"}) - camilla_mockquery_yaml.query.assert_called_with('ValidateConfig', arg='some: yaml\n') + camilla_mockquery_yaml.query.assert_called_with("ReadConfig", arg="rawyaml") + camilla_mockquery_yaml.config.validate({"some": "yaml"}) + camilla_mockquery_yaml.query.assert_called_with( + "ValidateConfig", arg="some: yaml\n" + ) camilla_mockquery_yaml.config.active() - camilla_mockquery_yaml.query.assert_called_with('GetConfig') + camilla_mockquery_yaml.query.assert_called_with("GetConfig") camilla_mockquery_yaml.config.previous() - camilla_mockquery_yaml.query.assert_called_with('GetPreviousConfig') + camilla_mockquery_yaml.query.assert_called_with("GetPreviousConfig")