From fd3d4ed9b54a79136c247ea8e51c6bcbfc3002fc Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Thu, 28 Mar 2024 15:03:19 +0100 Subject: [PATCH 01/10] refactor config: replaced 'config.toml' by _CONFIG_FILE --- src/config.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/config.py b/src/config.py index 3b1ab354..5ce1c3ff 100644 --- a/src/config.py +++ b/src/config.py @@ -4,6 +4,7 @@ class Config: _instance = None + _CONFIG_FILE = "config.toml" def __new__(cls): if cls._instance is None: @@ -13,11 +14,11 @@ def __new__(cls): def _load_config(self): # If the config file doesn't exist, copy from the sample - if not os.path.exists("config.toml"): - with open("sample.config.toml", "r") as f_in, open("config.toml", "w") as f_out: + if not os.path.exists(self._CONFIG_FILE): + with open("sample.config.toml", "r") as f_in, open(self._CONFIG_FILE, "w") as f_out: f_out.write(f_in.read()) - self.config = toml.load("config.toml") + self.config = toml.load(self._CONFIG_FILE) def get_config(self): return self.config @@ -163,5 +164,5 @@ def set_logging_prompts(self, value): self.save_config() def save_config(self): - with open("config.toml", "w") as f: + with open(self._CONFIG_FILE, "w") as f: toml.dump(self.config, f) From b3d8747e3bd97ce474ca69820752c41e1b33f11c Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Thu, 28 Mar 2024 23:35:07 +0100 Subject: [PATCH 02/10] test config: implement tests for Config class --- tests/test_config.py | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/test_config.py diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..966fa294 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,56 @@ +from src.config import Config +import os +import shutil +from pytest import fixture + + +class TestConfig: + + _TEST_CONFIG_FILE = "sample.config.toml" + + @fixture + def patch_config(self, mocker, tmpdir): + """ + Patch the class Config + Config._CONFIG_FILE is changed to point to the example config file + """ + assert os.path.isfile(self._TEST_CONFIG_FILE) + p = tmpdir + p = shutil.copy(self._TEST_CONFIG_FILE, p) + return mocker.patch("src.config.Config._CONFIG_FILE", p) + + @fixture + def patch_toml_load(self, mocker): + """ + Patch every use of toml.load in class Config + The return value from toml.load is changed to `{}` + """ + return mocker.patch("src.config.toml.load", return_value={}) + + def test_creation(self, patch_config): + assert Config() is not None + + def test_singleton(self, patch_config): + assert Config() == Config() + + def test_toml_load_called_once_during_multiple_access(self, patch_toml_load): + Config(), Config(), Config(), Config(), Config() + patch_toml_load.assert_called_once() + + def test_save_config_correctly_saves_config(self, patch_config): + # Read config from file and modify values + Config().set_bing_api_key("10random_bing_key01") + Config().set_logs_dir("10random_logs_dir01") + # (as of March 28th, setters save to file) + # Get the config that was written to file + config_saved = Config().get_config() + # Force loading of the config from the file by deleting singleton + Config()._instance = None + # Get the config loaded from file + config_loaded = Config().get_config() + + assert config_saved == config_loaded + + + + From 22e26e1e8c5f12e7d4df2f05efaf3b4dd1d07534 Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Fri, 29 Mar 2024 03:07:16 +0100 Subject: [PATCH 03/10] fix config: eliminate surperfluous file loading fix: eliminate the reloading of the config file at each call to Config. fix and refactor: create the setup fixture and add it to each tests fix: delete the instance before loading in test_save_then_load_keep_config_unchanged --- tests/test_config.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 966fa294..a72913a7 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -8,11 +8,17 @@ class TestConfig: _TEST_CONFIG_FILE = "sample.config.toml" + @fixture + def setup(self): + # Delete Config singleton + Config._instance = None + @fixture def patch_config(self, mocker, tmpdir): """ Patch the class Config - Config._CONFIG_FILE is changed to point to the example config file + Config._CONFIG_FILE is changed to a path towards a temporary file + The temporary file is a copy of _TEST_CONFIG_FILE """ assert os.path.isfile(self._TEST_CONFIG_FILE) p = tmpdir @@ -27,17 +33,17 @@ def patch_toml_load(self, mocker): """ return mocker.patch("src.config.toml.load", return_value={}) - def test_creation(self, patch_config): + def test_creation(self, setup, patch_config): assert Config() is not None - def test_singleton(self, patch_config): + def test_singleton(self, setup, patch_config): assert Config() == Config() - def test_toml_load_called_once_during_multiple_access(self, patch_toml_load): + def test_toml_load_called_once_during_multiple_access(self, setup, patch_config, patch_toml_load): Config(), Config(), Config(), Config(), Config() patch_toml_load.assert_called_once() - def test_save_config_correctly_saves_config(self, patch_config): + def test_save_config_correctly_saves_config(self, setup, patch_config): # Read config from file and modify values Config().set_bing_api_key("10random_bing_key01") Config().set_logs_dir("10random_logs_dir01") @@ -45,7 +51,7 @@ def test_save_config_correctly_saves_config(self, patch_config): # Get the config that was written to file config_saved = Config().get_config() # Force loading of the config from the file by deleting singleton - Config()._instance = None + Config._instance = None # Get the config loaded from file config_loaded = Config().get_config() From 15f57ba7b3d1818f0f550befc7bc840fe48e76bb Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Fri, 29 Mar 2024 03:29:27 +0100 Subject: [PATCH 04/10] refactor: put the load config logic in a function This allows the tests not to depend on the implementation --- tests/test_config.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index a72913a7..081fd9af 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -26,12 +26,12 @@ def patch_config(self, mocker, tmpdir): return mocker.patch("src.config.Config._CONFIG_FILE", p) @fixture - def patch_toml_load(self, mocker): + def patch_load_config(self, mocker): """ - Patch every use of toml.load in class Config - The return value from toml.load is changed to `{}` + Patch load_config in class Config + The return value from load_config is changed to `{}` """ - return mocker.patch("src.config.toml.load", return_value={}) + return mocker.patch("src.config.Config.load_config", return_value={}) def test_creation(self, setup, patch_config): assert Config() is not None @@ -39,9 +39,9 @@ def test_creation(self, setup, patch_config): def test_singleton(self, setup, patch_config): assert Config() == Config() - def test_toml_load_called_once_during_multiple_access(self, setup, patch_config, patch_toml_load): + def test_load_config_called_once_during_multiple_access(self, setup, patch_config, patch_load_config): Config(), Config(), Config(), Config(), Config() - patch_toml_load.assert_called_once() + patch_load_config.assert_called_once() def test_save_config_correctly_saves_config(self, setup, patch_config): # Read config from file and modify values From 6287bf13b56bdb2a06b37a19e0b06c622eb38230 Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Fri, 29 Mar 2024 13:40:51 +0100 Subject: [PATCH 05/10] refactor: rename save_config to dump_config --- devika.py | 2 +- src/config.py | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/devika.py b/devika.py index c03350d1..1f14811d 100644 --- a/devika.py +++ b/devika.py @@ -183,7 +183,7 @@ def set_settings(): data = request.json print("Data: ", data) config.config.update(data) - config.save_config() + config.dump_config() return jsonify({"message": "Settings updated"}) diff --git a/src/config.py b/src/config.py index 5ce1c3ff..d83ce026 100644 --- a/src/config.py +++ b/src/config.py @@ -85,11 +85,11 @@ def get_logging_prompts(self): def set_bing_api_key(self, key): self.config["API_KEYS"]["BING"] = key - self.save_config() + self.dump_config() def set_bing_api_endpoint(self, endpoint): self.config["API_ENDPOINTS"]["BING"] = endpoint - self.save_config() + self.dump_config() def set_google_search_api_key(self, key): self.config["API_KEYS"]["GOOGLE_SEARCH"] = key @@ -105,15 +105,15 @@ def set_google_search_api_endpoint(self, endpoint): def set_ollama_api_endpoint(self, endpoint): self.config["API_ENDPOINTS"]["OLLAMA"] = endpoint - self.save_config() + self.dump_config() def set_claude_api_key(self, key): self.config["API_KEYS"]["CLAUDE"] = key - self.save_config() + self.dump_config() def set_openai_api_key(self, key): self.config["API_KEYS"]["OPENAI"] = key - self.save_config() + self.dump_config() def set_gemini_api_key(self, key): self.config["API_KEYS"]["GEMINI"] = key @@ -129,40 +129,40 @@ def set_groq_api_key(self, key): def set_netlify_api_key(self, key): self.config["API_KEYS"]["NETLIFY"] = key - self.save_config() + self.dump_config() def set_sqlite_db(self, db): self.config["STORAGE"]["SQLITE_DB"] = db - self.save_config() + self.dump_config() def set_screenshots_dir(self, dir): self.config["STORAGE"]["SCREENSHOTS_DIR"] = dir - self.save_config() + self.dump_config() def set_pdfs_dir(self, dir): self.config["STORAGE"]["PDFS_DIR"] = dir - self.save_config() + self.dump_config() def set_projects_dir(self, dir): self.config["STORAGE"]["PROJECTS_DIR"] = dir - self.save_config() + self.dump_config() def set_logs_dir(self, dir): self.config["STORAGE"]["LOGS_DIR"] = dir - self.save_config() + self.dump_config() def set_repos_dir(self, dir): self.config["STORAGE"]["REPOS_DIR"] = dir - self.save_config() + self.dump_config() def set_logging_rest_api(self, value): self.config["LOGGING"]["LOG_REST_API"] = "true" if value else "false" - self.save_config() + self.dump_config() def set_logging_prompts(self, value): self.config["LOGGING"]["LOG_PROMPTS"] = "true" if value else "false" - self.save_config() + self.dump_config() - def save_config(self): + def dump_config(self): with open(self._CONFIG_FILE, "w") as f: toml.dump(self.config, f) From ee4dee9c44b36994543ca6d45684a034f97647cf Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Tue, 2 Apr 2024 23:45:57 +0200 Subject: [PATCH 06/10] fix: correct a test --- .gitignore | 3 +++ tests/test_config.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 57cef4a4..5a7db33f 100644 --- a/.gitignore +++ b/.gitignore @@ -159,5 +159,8 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ +# Visual Studio Code project settings +.vscode/ + notes.md data/ diff --git a/tests/test_config.py b/tests/test_config.py index 081fd9af..4fab1137 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -31,7 +31,7 @@ def patch_load_config(self, mocker): Patch load_config in class Config The return value from load_config is changed to `{}` """ - return mocker.patch("src.config.Config.load_config", return_value={}) + return mocker.patch("src.config.Config._load_config", return_value={}) def test_creation(self, setup, patch_config): assert Config() is not None From 934ade1dd79c2d0d1b9540a3fe39e4711d8c1ec1 Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Wed, 3 Apr 2024 00:04:22 +0200 Subject: [PATCH 07/10] refactor: add _SAMPLE_CONFIG_FILE to Config class --- src/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config.py b/src/config.py index d83ce026..a201707c 100644 --- a/src/config.py +++ b/src/config.py @@ -5,6 +5,7 @@ class Config: _instance = None _CONFIG_FILE = "config.toml" + _SAMPLE_CONFIG_FILE = "sample.config.toml" def __new__(cls): if cls._instance is None: @@ -15,7 +16,7 @@ def __new__(cls): def _load_config(self): # If the config file doesn't exist, copy from the sample if not os.path.exists(self._CONFIG_FILE): - with open("sample.config.toml", "r") as f_in, open(self._CONFIG_FILE, "w") as f_out: + with open(self._SAMPLE_CONFIG_FILE, "r") as f_in, open(self._CONFIG_FILE, "w") as f_out: f_out.write(f_in.read()) self.config = toml.load(self._CONFIG_FILE) From c9a1f9a37c687582a8b0269dde43284a9948b767 Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Thu, 4 Apr 2024 00:45:00 +0200 Subject: [PATCH 08/10] fix: change a few save_config to dump_config --- src/config.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/config.py b/src/config.py index a201707c..4f3e2bb1 100644 --- a/src/config.py +++ b/src/config.py @@ -94,15 +94,15 @@ def set_bing_api_endpoint(self, endpoint): def set_google_search_api_key(self, key): self.config["API_KEYS"]["GOOGLE_SEARCH"] = key - self.save_config() + self.dump_config() def set_google_search_engine_id(self, key): self.config["API_KEYS"]["GOOGLE_SEARCH_ENGINE_ID"] = key - self.save_config() + self.dump_config() def set_google_search_api_endpoint(self, endpoint): self.config["API_ENDPOINTS"]["GOOGLE_SEARCH"] = endpoint - self.save_config() + self.dump_config() def set_ollama_api_endpoint(self, endpoint): self.config["API_ENDPOINTS"]["OLLAMA"] = endpoint @@ -118,15 +118,15 @@ def set_openai_api_key(self, key): def set_gemini_api_key(self, key): self.config["API_KEYS"]["GEMINI"] = key - self.save_config() + self.dump_config() def set_mistral_api_key(self, key): self.config["API_KEYS"]["MISTRAL"] = key - self.save_config() + self.dump_config() def set_groq_api_key(self, key): self.config["API_KEYS"]["GROQ"] = key - self.save_config() + self.dump_config() def set_netlify_api_key(self, key): self.config["API_KEYS"]["NETLIFY"] = key From 9f76f57c0bee85ff9396fe076cdc69736636be41 Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Thu, 4 Apr 2024 00:58:35 +0200 Subject: [PATCH 09/10] fix: rename a test to be clearer --- tests/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index 4fab1137..8946f08e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -43,7 +43,7 @@ def test_load_config_called_once_during_multiple_access(self, setup, patch_confi Config(), Config(), Config(), Config(), Config() patch_load_config.assert_called_once() - def test_save_config_correctly_saves_config(self, setup, patch_config): + def test_dump_config_correctly_saves_config(self, setup, patch_config): # Read config from file and modify values Config().set_bing_api_key("10random_bing_key01") Config().set_logs_dir("10random_logs_dir01") From 4236cbda4405e17d273ac087a03ba5f594482676 Mon Sep 17 00:00:00 2001 From: Felix Auneau Date: Sat, 20 Apr 2024 17:55:06 +0200 Subject: [PATCH 10/10] clean: remove unnecessary empty lines --- tests/test_config.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 8946f08e..1a03edf6 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -55,8 +55,4 @@ def test_dump_config_correctly_saves_config(self, setup, patch_config): # Get the config loaded from file config_loaded = Config().get_config() - assert config_saved == config_loaded - - - - + assert config_saved == config_loaded \ No newline at end of file