From 19862c7b792b74bbb07728215c25dabebf54dad4 Mon Sep 17 00:00:00 2001 From: Erik Halt <112868973+erikhalt@users.noreply.github.com> Date: Fri, 22 Sep 2023 17:43:33 +0200 Subject: [PATCH 01/11] Updated README.md get started with commands Fixed new ability command example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1dfa35a..8ca62b8 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ pip install pyttman pyttman new app my_first_app # Create an Ability module with files from a template -pyttman new ability +pyttman new ability ability_name my_first_app # Run it in dev mode pyttman dev my_first_app From 8df9297b7c46c0aacd1692ef748e3963fa257a22 Mon Sep 17 00:00:00 2001 From: Simon Olofsson Date: Sat, 23 Sep 2023 11:49:43 +0200 Subject: [PATCH 02/11] Fixed build file --- devtools/build.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/devtools/build.py b/devtools/build.py index c383cb3..a1b3f7f 100644 --- a/devtools/build.py +++ b/devtools/build.py @@ -6,11 +6,13 @@ # Set the current working dir to parent directory sys.path.append(Path.cwd().parent.as_posix()) -os.chdir("..") -CATALOGS = ("build", "dist") +CATALOGS = ("build", "dist") BUILD_CMD = "python -m setup sdist bdist_wheel".split() + if __name__ == "__main__": - [shutil.rmtree(i) for i in CATALOGS] + subprocess.Popen(BUILD_CMD) + [shutil.rmtree(i) for i in CATALOGS if Path(i).exists()] subprocess.run(BUILD_CMD) + From 32aa663b9654519e81ff377eaf80ff93fc7bae74 Mon Sep 17 00:00:00 2001 From: Simon Olofsson Date: Sat, 23 Sep 2023 11:50:14 +0200 Subject: [PATCH 03/11] Improved create_environment script file; automatic call to 'build.py' if not already present; with instructions at the end to make getting up to speed easier. --- devtools/create_environment.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/devtools/create_environment.py b/devtools/create_environment.py index 30c500e..0c8473e 100644 --- a/devtools/create_environment.py +++ b/devtools/create_environment.py @@ -7,7 +7,11 @@ from time import sleep sys.path.append(Path.cwd().parent.as_posix()) -os.chdir("..") + +if not Path("setup.py").exists(): + print("You're not in the right directory. Run this script from the " + r"project's root directory, e.g. 'C:\users\your_user\projects\pyttman'.") + exit(-1) LAB_ENV_PATH = Path.cwd() / Path("dev_env") BUILD_OUTPUT_PATH = Path.cwd() / "dist" @@ -17,10 +21,7 @@ shutil.rmtree((LAB_ENV_PATH / "venv").as_posix()) if not Path("dist").exists(): - print("\nCannot create local testing environment as there is no " - "build generated for the current local version of Pyttman.", - "Run 'build.py' to create one.") - exit(-1) + subprocess.check_call("python devtools/build.py".split()) LAB_ENV_PATH.mkdir(exist_ok=True) os.chdir(LAB_ENV_PATH.as_posix()) @@ -39,5 +40,16 @@ subprocess.run(f"{venv_python} -m pip install multidict".split()) subprocess.run(f"{venv_python} -m pip install {package_file}".split()) - print("\nFinished! You can now create an app and start testing in " - f"{LAB_ENV_PATH.as_posix()}.") + clear_sc = "clear" if os.name == "posix" else "cls" + os.system(clear_sc) + + os.system("cls") + print("\nFinished! Here's how to get started:", + f"1. Activate the virtual environment:\n\tcd dev_env\n\tvenv/scripts/activate", + f"2. Run the command 'pyttman' to see available commands to the Pyttman CLI", + "3. If it's the first time you're running Pyttman, run 'pyttman new app {app_name}' to create a new project." + "4. Run 'pyttman dev {app_name}' to start the development server.", + "5. If you've made changes to the Pyttman framework which you want to test in your testing project, " + "run this script again. Your app will be left untouched, but the Pyttman version is upgraded to " + "your current HEAD in the Pyttman repo.", + sep="\n") From 893892215872b0092df9da540762f510319ee32e Mon Sep 17 00:00:00 2001 From: RadarJam Date: Sat, 23 Sep 2023 17:17:20 +0200 Subject: [PATCH 04/11] Test cases --- tests/core/mocksettings.py | 6 ++++++ tests/core/test_settings.py | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/core/mocksettings.py create mode 100644 tests/core/test_settings.py diff --git a/tests/core/mocksettings.py b/tests/core/mocksettings.py new file mode 100644 index 0000000..b44f8d3 --- /dev/null +++ b/tests/core/mocksettings.py @@ -0,0 +1,6 @@ +foo = "bar" + +d = { + "k1": "v1", + "k2":{"a":"a","b":"b"} +} \ No newline at end of file diff --git a/tests/core/test_settings.py b/tests/core/test_settings.py new file mode 100644 index 0000000..c8381a6 --- /dev/null +++ b/tests/core/test_settings.py @@ -0,0 +1,26 @@ +from tests.module_helper import PyttmanInternalBaseTestCase +from pyttman.core.internals import Settings + +from importlib import import_module +from . import mocksettings + +class PyttmanInternalSettingsPyttmanApp(PyttmanInternalBaseTestCase): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + settings_names = [i for i in dir(mocksettings) + if not i.startswith("_")] + settings_config = {name: getattr(mocksettings, name) + for name in settings_names} + self.settings = Settings(**settings_config) + + def test_read_settings_with_dictionary(self): + + self.assertTrue(self.settings.d.k2.a == "a") + self.assertTrue(self.settings.d["k2"].a == "a") + self.assertTrue(self.settings.d["k2"]["a"] == "a") + + self.assertTrue(self.settings.d.k1 == "v1") + self.assertTrue(self.settings.d["k1"] == "v1") + + self.assertTrue(self.settings.foo == "bar") + From 483f5ffdafde3d8cbf7c8af2f9d7e3b6d3f6b86b Mon Sep 17 00:00:00 2001 From: RadarJam Date: Sat, 23 Sep 2023 17:18:35 +0200 Subject: [PATCH 05/11] Dot notation can be used to read dicts in settings.py --- pyttman/core/internals.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pyttman/core/internals.py b/pyttman/core/internals.py index 9a4ac97..401b303 100644 --- a/pyttman/core/internals.py +++ b/pyttman/core/internals.py @@ -5,6 +5,9 @@ from dataclasses import dataclass, field from datetime import datetime from typing import Any +import json +from collections import UserDict + import pyttman from pyttman.core.containers import MessageMixin, Reply @@ -32,6 +35,12 @@ def depr_graceful(message: str, version: str): out = f"{message} - This was deprecated in version {version}." warnings.warn(out, DeprecationWarning) +class CustomUserDict(UserDict): + + # constructor + def __init__(self, dictionary): + self.data = dictionary + self.__dict__.update(dictionary) class Settings: """ @@ -60,10 +69,20 @@ def __init__(self, **kwargs): self.LOG_FORMAT: str | None = None self.LOG_TO_STDOUT: bool = False - [setattr(self, k, v) for k, v in kwargs.items() + [self.__set_attr(k, v) for k, v in kwargs.items() if not inspect.ismodule(v) and not inspect.isfunction(v)] + def __set_attr(self, k, v): + tmp = v + if isinstance(v, dict): + tmp = self.__dict2obj(v) + + setattr(self, k, tmp) + + def __dict2obj(self, dictionary): + return json.loads(json.dumps(dictionary), object_hook=CustomUserDict) + def __repr__(self): _attrs = {name: value for name, value in self.__dict__.items()} return f"Settings({_attrs})" From 480cdc35a1815ad850b570624bb41ce3a0537766 Mon Sep 17 00:00:00 2001 From: RadarJam Date: Sun, 24 Sep 2023 15:36:02 +0200 Subject: [PATCH 06/11] Removed Reduntant class --- pyttman/core/internals.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/pyttman/core/internals.py b/pyttman/core/internals.py index 401b303..c3017fd 100644 --- a/pyttman/core/internals.py +++ b/pyttman/core/internals.py @@ -35,13 +35,6 @@ def depr_graceful(message: str, version: str): out = f"{message} - This was deprecated in version {version}." warnings.warn(out, DeprecationWarning) -class CustomUserDict(UserDict): - - # constructor - def __init__(self, dictionary): - self.data = dictionary - self.__dict__.update(dictionary) - class Settings: """ Dataclass holding settings configured in the settings.py @@ -57,7 +50,8 @@ class Settings: aren't valid settings. """ - def __init__(self, **kwargs): + def __init__(self, dictionary={}, **kwargs): + self.__dict__.update(dictionary) self.APPEND_LOG_FILES: bool = True self.MIDDLEWARE: dict | None = None self.ABILITIES: list | None = None @@ -73,15 +67,18 @@ def __init__(self, **kwargs): if not inspect.ismodule(v) and not inspect.isfunction(v)] + def __getitem__(self, item): + return self.__dict__[item] + def __set_attr(self, k, v): tmp = v if isinstance(v, dict): tmp = self.__dict2obj(v) setattr(self, k, tmp) - + def __dict2obj(self, dictionary): - return json.loads(json.dumps(dictionary), object_hook=CustomUserDict) + return json.loads(json.dumps(dictionary), object_hook=Settings) def __repr__(self): _attrs = {name: value for name, value in self.__dict__.items()} From 6242f9b6d0aa168a3da2bdbabe2d588048a0eba6 Mon Sep 17 00:00:00 2001 From: RadarJam Date: Sun, 24 Sep 2023 15:37:41 +0200 Subject: [PATCH 07/11] Renamed method and set it as a class method --- pyttman/core/internals.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyttman/core/internals.py b/pyttman/core/internals.py index c3017fd..9066ecd 100644 --- a/pyttman/core/internals.py +++ b/pyttman/core/internals.py @@ -73,17 +73,17 @@ def __getitem__(self, item): def __set_attr(self, k, v): tmp = v if isinstance(v, dict): - tmp = self.__dict2obj(v) + tmp = Settings._dict_to_object(v) setattr(self, k, tmp) - def __dict2obj(self, dictionary): - return json.loads(json.dumps(dictionary), object_hook=Settings) - def __repr__(self): _attrs = {name: value for name, value in self.__dict__.items()} return f"Settings({_attrs})" + @staticmethod + def _dict_to_object(dictionary): + return json.loads(json.dumps(dictionary), object_hook=Settings) def _generate_name(name): """ From b30d1abd26f0c16a7c4d289bc5f61b648e927c11 Mon Sep 17 00:00:00 2001 From: RadarJam Date: Sun, 24 Sep 2023 15:55:45 +0200 Subject: [PATCH 08/11] Test suite is now using pytest test fixtures --- tests/core/test_settings.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/tests/core/test_settings.py b/tests/core/test_settings.py index c8381a6..405dd78 100644 --- a/tests/core/test_settings.py +++ b/tests/core/test_settings.py @@ -4,23 +4,26 @@ from importlib import import_module from . import mocksettings -class PyttmanInternalSettingsPyttmanApp(PyttmanInternalBaseTestCase): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - settings_names = [i for i in dir(mocksettings) - if not i.startswith("_")] - settings_config = {name: getattr(mocksettings, name) - for name in settings_names} - self.settings = Settings(**settings_config) +import pytest - def test_read_settings_with_dictionary(self): - - self.assertTrue(self.settings.d.k2.a == "a") - self.assertTrue(self.settings.d["k2"].a == "a") - self.assertTrue(self.settings.d["k2"]["a"] == "a") - - self.assertTrue(self.settings.d.k1 == "v1") - self.assertTrue(self.settings.d["k1"] == "v1") +@pytest.fixture +def mockSettings(): + settings_names = [i for i in dir(mocksettings) + if not i.startswith("_")] + settings_config = {name: getattr(mocksettings, name) + for name in settings_names} + + return Settings(**settings_config) + +def test_read_settings_with_dictionary(mockSettings): + settings = mockSettings + + assert settings.d.k2.a == "a" + assert settings.d["k2"].a == "a" + assert settings.d["k2"]["a"] == "a" + + assert settings.d.k1 == "v1" + assert settings.d["k1"] == "v1" - self.assertTrue(self.settings.foo == "bar") + assert settings.foo == "bar" From a9c43287e8f8ba4bae78e970d12085260ec76b89 Mon Sep 17 00:00:00 2001 From: RadarJam Date: Sun, 24 Sep 2023 16:09:01 +0200 Subject: [PATCH 09/11] Test Fixture generates MockSettings --- tests/core/mocksettings.py | 6 ------ tests/core/test_settings.py | 33 ++++++++++++++++++--------------- 2 files changed, 18 insertions(+), 21 deletions(-) delete mode 100644 tests/core/mocksettings.py diff --git a/tests/core/mocksettings.py b/tests/core/mocksettings.py deleted file mode 100644 index b44f8d3..0000000 --- a/tests/core/mocksettings.py +++ /dev/null @@ -1,6 +0,0 @@ -foo = "bar" - -d = { - "k1": "v1", - "k2":{"a":"a","b":"b"} -} \ No newline at end of file diff --git a/tests/core/test_settings.py b/tests/core/test_settings.py index 405dd78..a225584 100644 --- a/tests/core/test_settings.py +++ b/tests/core/test_settings.py @@ -2,28 +2,31 @@ from pyttman.core.internals import Settings from importlib import import_module -from . import mocksettings - import pytest @pytest.fixture def mockSettings(): - settings_names = [i for i in dir(mocksettings) - if not i.startswith("_")] - settings_config = {name: getattr(mocksettings, name) - for name in settings_names} + + mock_settings = { + "d":{ + "k1":"v1", + "k2":{ + "a":"a", + "b":"b" + } + }, + "foo":"bar" + } - return Settings(**settings_config) + return Settings(**mock_settings) def test_read_settings_with_dictionary(mockSettings): - settings = mockSettings - - assert settings.d.k2.a == "a" - assert settings.d["k2"].a == "a" - assert settings.d["k2"]["a"] == "a" + assert mockSettings.d.k2.a == "a" + assert mockSettings.d["k2"].a == "a" + assert mockSettings.d["k2"]["a"] == "a" - assert settings.d.k1 == "v1" - assert settings.d["k1"] == "v1" + assert mockSettings.d.k1 == "v1" + assert mockSettings.d["k1"] == "v1" - assert settings.foo == "bar" + assert mockSettings.foo == "bar" From 305697620a9a4068366c1bb2f2ad3ca159203c86 Mon Sep 17 00:00:00 2001 From: RadarJam Date: Mon, 25 Sep 2023 18:36:46 +0200 Subject: [PATCH 10/11] Fixed memory leak and renamed method --- pyttman/core/internals.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pyttman/core/internals.py b/pyttman/core/internals.py index 9066ecd..97b09ca 100644 --- a/pyttman/core/internals.py +++ b/pyttman/core/internals.py @@ -50,7 +50,9 @@ class Settings: aren't valid settings. """ - def __init__(self, dictionary={}, **kwargs): + def __init__(self, dictionary=None, **kwargs): + if dictionary is None: + dictionary = {} self.__dict__.update(dictionary) self.APPEND_LOG_FILES: bool = True self.MIDDLEWARE: dict | None = None @@ -63,14 +65,14 @@ def __init__(self, dictionary={}, **kwargs): self.LOG_FORMAT: str | None = None self.LOG_TO_STDOUT: bool = False - [self.__set_attr(k, v) for k, v in kwargs.items() + [self._set_attr(k, v) for k, v in kwargs.items() if not inspect.ismodule(v) and not inspect.isfunction(v)] def __getitem__(self, item): return self.__dict__[item] - def __set_attr(self, k, v): + def _set_attr(self, k, v): tmp = v if isinstance(v, dict): tmp = Settings._dict_to_object(v) From 5fd43a37ade93b50b8b4c9bd2161146fae50033c Mon Sep 17 00:00:00 2001 From: Simon Olofsson Date: Sat, 23 Dec 2023 00:58:01 +0100 Subject: [PATCH 11/11] added 'pytz' as a dependency, for TIME_ZONE setting --- pyttman/core/internals.py | 6 ++++-- requirements.txt | Bin 856 -> 784 bytes 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyttman/core/internals.py b/pyttman/core/internals.py index 97b09ca..bc8f39e 100644 --- a/pyttman/core/internals.py +++ b/pyttman/core/internals.py @@ -64,19 +64,21 @@ def __init__(self, dictionary=None, **kwargs): self.APP_NAME: str | None = None self.LOG_FORMAT: str | None = None self.LOG_TO_STDOUT: bool = False + self.STATIC_FILES_DIR: Path | None = None + self.TIME_ZONE: pytz.timezone = None [self._set_attr(k, v) for k, v in kwargs.items() if not inspect.ismodule(v) and not inspect.isfunction(v)] def __getitem__(self, item): - return self.__dict__[item] + return self.__dict__[item] def _set_attr(self, k, v): tmp = v if isinstance(v, dict): tmp = Settings._dict_to_object(v) - + setattr(self, k, tmp) def __repr__(self): diff --git a/requirements.txt b/requirements.txt index 41e646cb69b62fe85e847b312c11b2508a57ec70..733924ce239dc244ce3123d1017f1b140afc54e3 100644 GIT binary patch delta 54 zcmcb?Hi2!zhK)DwG0K-PR592Bp%H@t5F0b-fpGytK0`4>34`I}wM?p$zcHyywquq8 E0HVtc^#A|> delta 120 zcmbQhc7tug2E|l{B8C!%OolWdwgo~X1_K~AV$fr-1Y#owlZ`8%GEUycBqLwMkjhZV yPzofAfjWwzszGWjfGQ0qpJmdNF9n*G1B96jNesqd6^39_%s?s_j3<9&k_P}Nn;2LC