readable validation with python regex and simple logic I have come with such a code for checking IP addresses. But would like to know how clean and good it is from 1 to 10. What I care about are readability and simplicity. Please give me any feedback.
Should I go here with unittests?
import re
import logging

logging.basicConfig(level=logging.ERROR)

pattern = re.compile(r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$')

def is_in_bounds(s): 
    return int(s) &gt;= 0 and int(s) &lt;= 255

def is_leading_zeros(s):
    if s is not '0':
        return s[0] is not '0'
    return True

def validate_ipv4(ipv4: str, expectFail: bool = False) -&gt; bool:
    logging.info(f&quot;Validating IP '{ipv4}' START.&quot;)

    try:
        match = pattern.match(ipv4)
        assert(match)

        groups = match.groups()
        assert(len(groups)==4)

        for s in match.groups():
            assert(is_leading_zeros(s))
            assert(is_in_bounds(s))
    
        logging.info(f&quot;Validating IP '{ipv4}' SUCCESS.&quot;)

    except AssertionError:
        logging.info(f&quot;Validating IP '{ipv4}' FAILED.&quot;, exc_info=True)
        if not expectFail:
            raise


if __name__ == '__main__':
    octets = []
    octets.extend(range(0,3))
    octets.extend(range(9,12))
    octets.extend(range(98,102))
    octets.extend(range(198,202))
    octets.extend(range(250,256))

    for i in octets:
        for j in octets:
            for k in octets:
                for l in octets:
                    validate_ipv4(f'{i}.{j}.{k}.{l}')

    octets = []
    octets.extend(range(-3,0))
    octets.extend(range(256, 260))  

    exceptions = []

    for i in octets:
        for j in octets:
            for k in octets:
                for l in octets:
                    validate_ipv4(f'{i}.{j}.{k}.{l}', True)
When to use is not
Your usage of is not is incorrect. You want to use == when you're testing equality, like when you check if s doesn't equal a '0'. You want to use is not when checking for identity, like when you're ensuring two variables are of the same object.
def is_leading_zeros(s: str) -&gt; bool:
    if s != '0':
        return s[0] != '0'
    return True

Clearer comparisons
Python has a syntax that allows you to do this:
def is_in_bounds(s: str) -&gt; int:
    return 0 &lt;= int(s) &lt;= 255

Type Hints
You should add types hints to your functions, which display what types are accepted and returned from your functions. See above for an example of this.