From ab57a849569f57bee7078a9aa5aefe88eb59d392 Mon Sep 17 00:00:00 2001 From: Kodjo Sossouvi Date: Thu, 18 Jul 2019 23:55:14 +0200 Subject: [PATCH] Starting Sheerka, learning Python --- .editorconfig | 15 ++++++ .gitignore | 4 ++ Makefile | 4 ++ app.py | 3 ++ core/__init__.py | 0 core/concept.py | 36 +++++++++++++++ core/sheerka.py | 84 ++++++++++++++++++++++++++++++++++ docs/syntax.md | 103 ++++++++++++++++++++++++++++++++++++++++++ tests/test_sheerka.py | 34 ++++++++++++++ 9 files changed, 283 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 app.py create mode 100644 core/__init__.py create mode 100644 core/concept.py create mode 100644 core/sheerka.py create mode 100644 docs/syntax.md create mode 100644 tests/test_sheerka.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..58aa6df --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +[*] +charset=utf-8 +end_of_line=lf +insert_final_newline=false +indent_style=space +indent_size=4 + +[*.json] +indent_style=space +indent_size=2 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..131ecd2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +venv +.pytest_cache +.idea +__pycache__ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0c5c8ec --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +test: + py.test tests + +.PHONY: init test \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..25e3d2a --- /dev/null +++ b/app.py @@ -0,0 +1,3 @@ + +f = open("MyFirstFile.txt", "w+") +f.close() \ No newline at end of file diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/concept.py b/core/concept.py new file mode 100644 index 0000000..bc4315e --- /dev/null +++ b/core/concept.py @@ -0,0 +1,36 @@ +class Concept: + """ + Default concept object + A concept is a the base object of our universe + Everything is a concept + """ + + concepts_id = 0 + + def __init__(self, name, is_builtin=False): + self.name = name + self.is_builtin = is_builtin + self.pre = None # list of pre conditions before calling the main function + self.post = None # list of post conditions after calling the main function + self.main = None # main method + self.id = Concept.concepts_id + Concept.concepts_id = Concept.concepts_id + 1 + + self.props = [] # list of Property for this concept + self.functions = {} # list of helper functions + + def __str__(self): + return f"({self.id}){self.name}" + + def __repr__(self): + return f"({self.id}){self.name}" + + +class Property: + """ + Defines a behaviour of Concept + """ + + def __init__(self, concept, value): + self.concept = concept + self.value = value diff --git a/core/sheerka.py b/core/sheerka.py new file mode 100644 index 0000000..cee0110 --- /dev/null +++ b/core/sheerka.py @@ -0,0 +1,84 @@ +import os +from dataclasses import dataclass + +from core.concept import Concept + + +class Singleton(type): + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + +@dataclass +class ReturnValue: + """ + Class that handle the return of a concept + To avoid using the try/except pattern for each and every call + To give context (ie return message) even when the call is successful + """ + status: bool + value: Concept + message: str = None + + +class Sheerka(Concept, metaclass=Singleton): + """ + Main controller for the project + """ + + NAME = "Sheerka" + UNKNOWN_CONCEPT_NAME = "Unknown Concept" + ERROR_CONCEPT_NAME = "Error" + SUCCESS_CONCEPT_NAME = "Success" + + def __init__(self): + super().__init__(Sheerka.NAME) + + # ist of all concepts known be the system + self.concepts = [] + + # At a given point of time, (TODO: in a specific contexts), somme concepts are instantiated + # ex: File is a concept, but File('foo.txt') is an instance + self.instances = [] # list of actual instances + + # List of the known rules by the system + # ex: hello => say('hello') + self.rules = [] + + self.create_builtin_concept() + + def create_builtin_concept(self): + self.concepts.append(self) + self.concepts.append(Concept(Sheerka.UNKNOWN_CONCEPT_NAME)) + self.concepts.append(Concept(Sheerka.SUCCESS_CONCEPT_NAME)) + self.concepts.append(Concept(Sheerka.ERROR_CONCEPT_NAME)) + + def initialize(self, root_folder): + # create the folder configuration folder if needed + try: + if not os.path.exists(root_folder): + os.makedirs(root_folder) + except IOError as e: + return ReturnValue(False, self.get_concept(Sheerka.ERROR_CONCEPT_NAME, True), e) + + return ReturnValue(True, self.get_concept(Sheerka.SUCCESS_CONCEPT_NAME, True)) + + def get_concept(self, name, is_builtin=False): + for concept in self.concepts: + if concept.name == name and concept.is_builtin == is_builtin: + return concept + return self.concepts[1] + + @staticmethod + def concept_equals(concept1, concept2): + if concept1 is None and concept2 is None: + return True + + if concept1 is None or concept2 is None: + return False + + return concept1.id == concept2.id diff --git a/docs/syntax.md b/docs/syntax.md new file mode 100644 index 0000000..ccd5b39 --- /dev/null +++ b/docs/syntax.md @@ -0,0 +1,103 @@ +#### Base syntax + +How to define a new concept + + + concept newFile: + + props: + name + + pre: + import os + os.path.isfile(name) == False + + + post: + import os + os.path.isfile(name) == True + + main: + f = open(name) + f.close() + __context__.add(newFile, name, f) + +example + + create a new file named MyFirstFile.txt => newFile("MyFirstFile.txt") + + + +concept newFile: + + props: + name + path + + pre: + import os + os.path.isfile(path) == False + + + post: + import os + os.path.isfile(path) == True + + main: + import os + f = open(name) + f.close() + sheeka.add(File(name, path)) + + +concept open: + + pre: + self.is_opened == False + + post: + self.is_opened == True + + main: + self.open + + +open the door => d = get_instance(door) && get_concept(open).call(d) + + +concept File: + + props: + name + path + + def open(): + + + def close(): + +open the file toto.txt => get_concept(open).call(File(path="toto.txt", name="toto.txt)) + +concept is_the_opposite: + + props: + a, b + + test: + a.pre == not b.pre && a.post == b.post + + +print all concepts + +concepts +print all + +concept print: + + main: + print(self) + +concept all: + + main: + self.find_all() \ No newline at end of file diff --git a/tests/test_sheerka.py b/tests/test_sheerka.py new file mode 100644 index 0000000..07825be --- /dev/null +++ b/tests/test_sheerka.py @@ -0,0 +1,34 @@ +import os + +from core.concept import Concept +from core.sheerka import Sheerka + + +def test_root_folder_is_created_after_initialization(): + root_folder = "init_folder" + + return_value = Sheerka().initialize(root_folder) + assert return_value.status, "initialisation should be successful" + assert Sheerka().concept_equals(return_value.value, Sheerka().get_concept("success")) + assert os.path.exists(root_folder), "init folder should be created" + + +def test_lists_of_concepts_is_initialized(): + root_folder = "init_folder" + + Sheerka().initialize(root_folder) + assert len(Sheerka().concepts) > 1 + + +def test_null_concept_are_equals(): + concept1 = Concept("test1") + concept2 = Concept("test2") + concept3 = Concept("test3") + + assert not Sheerka.concept_equals(concept1, None) + assert not Sheerka.concept_equals(None, concept1) + assert not Sheerka.concept_equals(concept1, concept2) + assert not Sheerka.concept_equals(concept1, concept3) + + assert Sheerka.concept_equals(None, None) + assert Sheerka.concept_equals(concept1, concept1)