<reponame>Tremeschin/Yatch
# WIP file

from Yatch import *

# You can use decorators if you want
# The following syntax equals to "action = Yatch(action)"

@Yatch
def action():
    ...




# Every non-sent required argument defaults to None!!

def other_command(required1, required2, default=2):
    ...
    
other_command = Yatch(other_command)
other_command()



# You can (re)name your commands instead of using the function's name,
# and also Default that command if no other command was given.

@Yatch(CommandName="Ohm", Default=False)
def alternating_current_ohms_law(U: complex, Z: complex, I: complex):
    ...



# "..." means required!! Also example for classes

class Calculator:
    def sum(self, A: float=..., B: float=...):
        print(A + B)

    def complex_conjugate(self, C: complex=...):
        print(C.real - 1j*C.imag)

    @Yatch
    def inverse(self, x):
        print(1/self.x)

Calc = Calculator()

# Sending the decorator arguments to Yatch works a bit silly
Yatch(Calc.sum)
Yatch(CommandName="conj")(Calc.complex_conjugate)
Yatch.Run()
Calc.inverse(3)

# Yatch's full functionality is achieved with YatchArguments

@Yatch
def fibonacci(
    N = YatchArgument(
        Type=int, Default=...,
        Restriction=YatchRestrictions.Positive,
        Help="Nth Number of the Fibonacci Sequence",
    ), 
):
    ...

#
# Some other possible builtin restrictions:
# - InsideRange(A, B)
# - InList(L)
# - PathExists
# - GreaterThan(N)
#
# There are many more restrictions, peek at the Source Code!!
#



# Input values as lists
def sum_of_numbers(numbers: Yarg(float, ..., List=True)):
    print(sum(numbers))




# You can run commands directly:
Yatch.Run("command1 --arg1 value --arg2 yes command2 --arg1 3 4 5 6")

# Or for less entropy
Yatch.Run(["Already", "--parsed", "list", "of", "--commands", "and", "Arguments"])

# It also accepts a Path to a file!
Yatch.Run("/some/input/file/multiline/no/problem.txt")

# Just Yatch.Run() defaults to the sys.argv input




# An argument can have many identifiers
# python main.py command -I file1 -Include file2
# If you do not send any identifier it defaults to f"--{variable_name}"
# or f"-{variable_name}" for single letter ones
Yarg(..., ..., Identifiers=["-I", "-Include"])





# # How argument parsing works:

# - Yatch first splits the incoming (final) string into the known commands, then for each command it parses its arguments.

# - If a value is a known argument name in `Identifiers` then it keeps adding what it finds next to a list of input values. 

# - If `List=True` it keeps this dictionary otherwise checks for `len()==1` and `value=values[0]`


# # How typing and restriction works

# - Yatch iterates on all incoming arguments and calls `Restriction(Type(Value))` for each value.







## ● Advanced Level

# Restrictions and Type can be any callable

def some_complex_restriction(N):
    """I have no clue what this does but it returns a bool"""
    A = sum([1/x for x in range(int(N)) if x != 0])
    return A + N**0.5 > 2*N

# Yarg is alias for YatchArgument
Yarg(float, Restriction=some_complex_restriction)

# For your restrictions to have "pretty printing" they must 
# implement .__str__() like ours, consider using _RestrictionFactory




# Yatch automatically sets the input value as self.{Name} attributes
# Trivia: This was the main reason I coded Yatch :)
class CompressionAlgorithm:
   
    def Compress(self,
        InputPath:            Yarg(Path, ...),
        OutputFile:           Yarg(Path, ...),
        DictionarySize:       Yarg( int, ...),
        CompressionLevel:     Yarg( int, ...),
        ExperimentalFeatureA: Yarg(bool, ...),
        ExperimentalFeatureB: Yarg(bool, ...),
        ExperimentalFeatureC: Yarg(bool, ...),
        ExperimentalFeatureD: Yarg(bool, ...),
    ):
        # Instead of painfully doing:
        #
        # | self.InputPath = Path(InputPath)
        # | assert self.InputPath.exists(), "Input Path does not exist"
        # |
        # | self.OutputFile = OutputFile
        # | assert self.OutputFile.exists(), "Output file does not exist"
        # |
        # | self.DictionarySize = DictionarySize
        # | assert 0 < self.DictionarySize < 1024, "Not enough RAM"
        # |
        # | self.CompressionLevel = CompressionLevel
        # | assert 0 < self.CompressionLevel < 3, "Levels supported: [0..3]"
        # |
        # | self.ExperimentalFeatureA = ExperimentalFeatureA
        # | self.ExperimentalFeatureB = ExperimentalFeatureB
        # | self.ExperimentalFeatureC = ExperimentalFeatureC
        # | self.ExperimentalFeatureD = ExperimentalFeatureD
        # | ...
        #
        # We already have them!!
        print(f"Input  Path is [{self.InputPath}]")
        print(f"Output File is [{self.OutputFile}]")
        
        # Just a matter of sending Restrictions=YatchRestrictions.*
        # for matching valid values.

        # You can send AttributeName= on the Yarg to set that to
        # self.{AttributeName} rather than self.{VarName}
        ...

C = CompressionAlgorithm()
Yatch(C.Compress)

# You can disable this by passing "ClassAttributes=False" to Yatch()




