|  | @@ -1,205 +1,68 @@
 | 
	
		
			
				|  |  | -from .exception import pini_exception
 | 
	
		
			
				|  |  | -from .exception import pini_type_exception
 | 
	
		
			
				|  |  |  from .exception import pini_syntax
 | 
	
		
			
				|  |  | -from .line import line
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class key(line):
 | 
	
		
			
				|  |  | -    def __init__(self):
 | 
	
		
			
				|  |  | -        super().__init__()
 | 
	
		
			
				|  |  | -        self.__value: str | int | float | None = None
 | 
	
		
			
				|  |  | +class key:
 | 
	
		
			
				|  |  | +    def __init__(
 | 
	
		
			
				|  |  | +        self,
 | 
	
		
			
				|  |  | +        name: str,
 | 
	
		
			
				|  |  | +        content: str | int | float | bool | None = None,
 | 
	
		
			
				|  |  | +        comment: str | None = None
 | 
	
		
			
				|  |  | +    ):
 | 
	
		
			
				|  |  | +        self.name = name
 | 
	
		
			
				|  |  | +        self.content = content
 | 
	
		
			
				|  |  | +        self.comment = comment
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @property
 | 
	
		
			
				|  |  | -    def value(self) -> str | int | float | None:
 | 
	
		
			
				|  |  | -        return self.__value
 | 
	
		
			
				|  |  | +    def name(self) -> str:
 | 
	
		
			
				|  |  | +        return self.__name
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @property
 | 
	
		
			
				|  |  | -    def value_type(self) -> str:
 | 
	
		
			
				|  |  | -        if self.__value is None:
 | 
	
		
			
				|  |  | -            return "None"
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        return type(self.__value).__name__
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    @value.setter
 | 
	
		
			
				|  |  | -    def value(self, target: str | int | float | None) -> None:
 | 
	
		
			
				|  |  | -        if type(target) is str:
 | 
	
		
			
				|  |  | -            self.__value = target.strip()
 | 
	
		
			
				|  |  | -            return
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | -        if type(target) is int or type(target) is float or target is None:
 | 
	
		
			
				|  |  | -            self.__value = target
 | 
	
		
			
				|  |  | -            return
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        raise TypeError("Value must be str, int, float or None.")
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def get(self, empty: str | int | float | None) -> str | int | float | None:
 | 
	
		
			
				|  |  | -        checked = type(empty) is str
 | 
	
		
			
				|  |  | -        checked = checked or type(empty) is int
 | 
	
		
			
				|  |  | -        checked = checked or type(empty) is float
 | 
	
		
			
				|  |  | -        checked = checked or empty is None
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if not checked:
 | 
	
		
			
				|  |  | -            raise TypeError("Default value for empty key type is not valid.")
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if self.__value is None:
 | 
	
		
			
				|  |  | -            return empty
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if type(empty) is not type(self.__value):
 | 
	
		
			
				|  |  | -            error = "Type of the " + self.name + " is not valid. "
 | 
	
		
			
				|  |  | -            error = error + "It must be in " + type(empty).__name__ + ". "
 | 
	
		
			
				|  |  | -            error = error + "It is " + type(self.__value).__name__ + "."
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            raise pini_type_exception(error)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        return self.__value
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def render(self) -> str:
 | 
	
		
			
				|  |  | -        comment = ""
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | -        if self.comment is not None:
 | 
	
		
			
				|  |  | -            comment = "# " + self.comment
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if self.name is None:
 | 
	
		
			
				|  |  | -            return comment
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        content = self.name + " = "
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if self.__value is not None:
 | 
	
		
			
				|  |  | -            content = content + str(self.__value)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if self.__value is None:
 | 
	
		
			
				|  |  | -            content = "### " + content
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | -        if len(comment) > 0:
 | 
	
		
			
				|  |  | -            content = content + (" " * (4 - (len(content) % 4)))
 | 
	
		
			
				|  |  | -            content = content + comment
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        return content
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def clone(self):
 | 
	
		
			
				|  |  | -        copy = key()
 | 
	
		
			
				|  |  | -        copy.name = self.name
 | 
	
		
			
				|  |  | -        copy.value = self.value
 | 
	
		
			
				|  |  | -        copy.comment = self.comment
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        return copy
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -class parse_key():
 | 
	
		
			
				|  |  | -    def __new__(cls, line: str) -> key:
 | 
	
		
			
				|  |  | -        target = key()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if type(line) is not str:
 | 
	
		
			
				|  |  | -            raise TypeError("Line must be in str type.")
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        cls.__init__(target, cls, line)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        return target
 | 
	
		
			
				|  |  | +    def content(self) -> str | int | float | bool | None:
 | 
	
		
			
				|  |  | +        return self.__content
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def __init__(self, cls, line: str) -> None:
 | 
	
		
			
				|  |  | -        line = line.lstrip()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if len(line) == 0:
 | 
	
		
			
				|  |  | -            return
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if line[0:3] == "###":
 | 
	
		
			
				|  |  | -            cls.__parse_blank(self, cls, line[3:])
 | 
	
		
			
				|  |  | -            return
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        cls.__parse_normal(self, cls, line)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def __split_comment(line: str) -> [str | None, str | None]:
 | 
	
		
			
				|  |  | -        line = line.lstrip()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if len(line) == 0:
 | 
	
		
			
				|  |  | -            return None, None
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        comment = None
 | 
	
		
			
				|  |  | -        content = line.rsplit()
 | 
	
		
			
				|  |  | -        comment_position = line.find("#")
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if comment_position != -1:
 | 
	
		
			
				|  |  | -            comment = line[comment_position + 1:]
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if comment[0] == " ":
 | 
	
		
			
				|  |  | -                comment = comment[1:]
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if len(comment) == 0:
 | 
	
		
			
				|  |  | -                comment = None
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            content = line[:comment_position].rsplit()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if len(content) == 0:
 | 
	
		
			
				|  |  | -            content = None
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        return content, comment
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def __parse_blank(self, cls, line: str) -> None:
 | 
	
		
			
				|  |  | -        content, comment = cls.__split_comment(line)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        where = content.find("=")
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if where != -1:
 | 
	
		
			
				|  |  | -            content = content[:where].rsplit()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        self.name = content
 | 
	
		
			
				|  |  | -        self.value = None
 | 
	
		
			
				|  |  | -        self.comment = comment
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def __split_comment(cls, line: str) -> [str | None, str | None]:
 | 
	
		
			
				|  |  | -        name = line
 | 
	
		
			
				|  |  | -        value = None
 | 
	
		
			
				|  |  | -        value_position = line.find("=")
 | 
	
		
			
				|  |  | +    @property
 | 
	
		
			
				|  |  | +    def comment(self) -> str | None:
 | 
	
		
			
				|  |  | +        return self.__comment
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if value_position == -1:
 | 
	
		
			
				|  |  | -            return name, value
 | 
	
		
			
				|  |  | +    @name.setter
 | 
	
		
			
				|  |  | +    def name(self, target: str) -> None:
 | 
	
		
			
				|  |  | +        target = target.strip()
 | 
	
		
			
				|  |  |          
 | 
	
		
			
				|  |  | -        name = line[:value_position].rsplit()
 | 
	
		
			
				|  |  | -        value = line[value_position + 1:].lstrip()
 | 
	
		
			
				|  |  | -        value = cls.__parse_value(cls, value)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        return name, value
 | 
	
		
			
				|  |  | +        if len(target) == 0:
 | 
	
		
			
				|  |  | +            raise pini_syntax("Key name lenght could not be blank.")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def __parse_value(cls, line: str) -> str | None:
 | 
	
		
			
				|  |  | -        line = line.strip()
 | 
	
		
			
				|  |  | +        self.__name = target
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if len(line) == 0:
 | 
	
		
			
				|  |  | -            return None
 | 
	
		
			
				|  |  | +    @content.setter
 | 
	
		
			
				|  |  | +    def content(self, target: str | int | float | bool | None) -> None:
 | 
	
		
			
				|  |  | +        self.__content = target
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if cls.is_number(line):
 | 
	
		
			
				|  |  | -            as_float = float(line)
 | 
	
		
			
				|  |  | -            as_integer = int(line)
 | 
	
		
			
				|  |  | +    @comment.setter
 | 
	
		
			
				|  |  | +    def comment(self, target: str) -> None:
 | 
	
		
			
				|  |  | +        self.__comment = target
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if as_float == as_integer:
 | 
	
		
			
				|  |  | -                return as_integer
 | 
	
		
			
				|  |  | +    def __render_content(self) -> str:
 | 
	
		
			
				|  |  | +        if type(self.content) is int or type(self.content) is float:
 | 
	
		
			
				|  |  | +            return str(self.content)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            return as_float
 | 
	
		
			
				|  |  | +        if type(self.content) is str:
 | 
	
		
			
				|  |  | +            return "\"" + self.content + "\""
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if line[0] == "'" or line[0] == "\"":
 | 
	
		
			
				|  |  | -            start = line[0]
 | 
	
		
			
				|  |  | +        if type(self.content) is bool:
 | 
	
		
			
				|  |  | +            return "True" if self.content else "False"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if line[-1] != start:
 | 
	
		
			
				|  |  | -                raise pini_syntax("String without stop.")
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            line = line[1:-1]
 | 
	
		
			
				|  |  | -            where = 0
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            while True:
 | 
	
		
			
				|  |  | -                where = line.find(start, where)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if where == -1:
 | 
	
		
			
				|  |  | -                    break
 | 
	
		
			
				|  |  | +        if self.content is None:
 | 
	
		
			
				|  |  | +            return "None"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                if line[where - 1] != "\\":
 | 
	
		
			
				|  |  | -                    raise pini_syntax("String stopper not expected.")
 | 
	
		
			
				|  |  | +        raise TypeError("Key content must be int, float, string or None.")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        return line 
 | 
	
		
			
				|  |  | +    def render(self) -> str:
 | 
	
		
			
				|  |  | +        result = self.name + " = "
 | 
	
		
			
				|  |  | +        result = result + self.__render_content()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def is_number(value: str) -> bool:
 | 
	
		
			
				|  |  | -        if value[0] == "-":
 | 
	
		
			
				|  |  | -            value = value[1:]
 | 
	
		
			
				|  |  | +        if self.comment is not None:
 | 
	
		
			
				|  |  | +            result = result + " # " + self.__comment
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        value = value.replace(".", "")
 | 
	
		
			
				|  |  | -        return value.isnumeric()
 | 
	
		
			
				|  |  | +        return result
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def __parse_normal(self, cls, line: str) -> None:
 | 
	
		
			
				|  |  | -        content, comment = cls.__split_comment(line)
 | 
	
		
			
				|  |  | -        name, value = cls.__split_content(cls, content) 
 | 
	
		
			
				|  |  | +    def __str__(self) -> str:
 | 
	
		
			
				|  |  | +        return self.render()
 |