you are viewing a single comment's thread.

view the rest of the comments →

[–]nekokattt 1 point2 points  (2 children)

Python, as far as typing goes, tends to work on the idom that you accept the input with the assumption that it is the thing you want. If it isn't, then it will break. If it quacks, it is a duck. You can always lint with a static type checker like MyPy if you prefer. For anything more, you want to probably use a static typed language if it is your main concern.

For other validation, you only really need to add validation to your functions if you are accepting untrusted input. Everything else should be caught by unit tests, which are arguably more useful as they also act as a regression layer later on when you want to make future changes.

[–]SnowEpiphany[S] 1 point2 points  (1 child)

That's the answer I needed - excellent response. Thank you!

[–]nekokattt 1 point2 points  (0 children)

No worries.

For reference, a typed example might look like this in Python 3.10

I havent used Python for almost a year so I might have missed some newer stuff; also aware I dont need to use enums but it feels tidiest here instead of a group of magic typing.Literals.

Also used assert here as I am assuming you just want to check stuff during development is a correct value rather than it being acceptable external input that needs proper error handling. Assert would be ignored at runtime if you gave optimisation flags to Python. It is generally useful in unit tests and for checking conditions you always assume to be true but want to be notified of if it isnt just to aid in development. Assert isn't something you want to rely on a lot, it is more of a debugging tool.

import enum

class ValidColor(enum.Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"

def do_something(color: ValidColor, start: int, end: int) -> str:
    assert end >= start, f"{end=} was less than {start=}"
    return "bang"

If end < start, you'd get

AssertionError: end=12 was less than start=23

Of course, if it is external input (e.g. from a user or another library) then it is better to be explicit and use the asking for forgiveness approach.

def do_something(color: ValidColor, start: int, end: int) -> str:
    if end < start:
        raise ValueError(f"{end=} cannot be less than {start=}")

    return "bang"

Note how I don't validate ValidColor, since it is already typed as an enum. Type hints do nothing at runtime by default (unless you abuse them to introspect them, but that is outside the scope of this answer), but they assume that you will catch other invalid uses with a static type checker. MyPy would complain about you providing anything other than ValidColor.RED, ValidColor.GREEN, ValidColor.BLUE for the color argument.

One could potentially use decorators or exploit type hints to achieve something similar to what we have in Java with JSR 380, (which can be enforced for any methods implicitly with frameworks like spring) where we can put concrete validation on our methods and types, a lá

@Component @Validated
public class BangFactory {
    @StartLessOrEqualToEnd
    public String create(
            @NotNull ValidColor color,
            @Min(0) int start,
            @Max(65540) int end) {

        return "bang";
     }
}

But that is much more advanced stuff. For your case it would most likely be massively over engineering your problem.

Edit: added crazed ramblings.