Skip to content

kaliv0/pypermissive

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Permissive Path


PyPermissive

tests Python 3.x PyPI Downloads License

Validation library in Python, modeled after Pydantic


Example

Inherit from BaseModel and describe required types.
PyPermissive supports validation for primitive types:

class Employee(BaseModel):
    employee_id: int
    name: str
    salary: float
    elected_benefits: bool = False
    
employee = Employee(
    employee_id=1,
    name="Foo Bar",
    salary=123_000.00,
    elected_benefits=True,
)

collections:

class Book(BaseModel):
    characters: dict[str, str]
    chapters: list[str]
    
book = Book(
    characters={"Pelleas": "he", "Melisande": "she"},
    chapters=["Beginning", "Middle", "End"]
)

unions, classes and fields.


Fields are similar to pydantic with one caveat: you need to give value type explicitly:

class User(BaseModel):
    name: Field(type=str, default="Jimmie", frozen=True)
    age: Field(type=int, gt=18, lt=35)
    id: Field(type=UUID, default_factory=uuid4)
    email: Field(type=str, pattern=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+[.][a-zA-Z0-9-.]+$")
    nickname: Field(type=str, min_length=6, max_length=12)
    PIN: Field(type=str, field_validator=lambda x: x.isdigit())

You can also use decorators:
@ComputedField (invoke only from instances) and @ComputedClassField (invoke both on class and instance level)

class Thesis:
    BAZZ = ["1", "2", "3"]

    def __init__(self):
        self.fizz = [1, 2, 3, 4, 5]
        self.buzz = [6, 7, 8, 9]

    @ComputedField
    def foo(self):
        return [val for val in itertools.product(self.fizz, self.buzz)]

    @ComputedClassField
    def bar(self):
        return list(itertools.permutations(self.BAZZ))

    

The library supports @validate_call that checks both argument and return types:

@validate_call
def some_func(delimiter: str, count: int, numbers: list[int]) -> str:
    return (delimiter * count).join([str(d) for d in numbers])

@Interface checks on a class-definition level if the decorated class implements all described methods with the specified signature

class MyInterface:
    def pow(self, x: int, y: int) -> int: ...


@Interface(MyInterface)
class Powerful:
    def pow(self, x: int, y: int) -> int:
        return x ** y
class OtherInterface:
    def moo(self): ...


@Interface(MyInterface, OtherInterface)
class Frankenstein:
    def pow(self, x: int, y: int) -> int:
        return x ** y

    def moo(self):
        return "Yeah Mr. White..."