Python calculator taking string input
On top of the other very good answers:

Code organisation and tests

It is a good idea to separate the logic about input/output from the logic computing results. In your case, you could have a function taking an expression as a string and returning a number.

This makes your code easier to understand, easier to reuse and more testable.

Also, it is a good habit to put your code actually calling the logic behind an if __name__ == "__main__": guard.

All of this taken into account, you have:

def eval_math_expr(expr):
    negate = False
    while True:
        try: 
            if expr[0] == '-': #for negative numbers
                negate = True #because here the numbers are string format
                expr = expr[1:]
            number1 = Test4Num(expr)[0]
            if negate == True:
                number1 = -number1
                negate = False
            end_number1 = Test4Num(expr)[1]
            expr = expr[end_number1:]
            if expr == '':
                return number1
            op = expr[0]
            expr = expr[1:]
            number2 = Test4Num(expr)[0]
            end_number2 = Test4Num(expr)[1]
            result = operation(op, number1, number2)
            number1 = result
            expr = str(number1) + expr[end_number2:]
        except Exception as e:
            print(e)
            break
    return number1


if __name__ == '__main__':
    interactive = False
    if interactive:
        expr = input('Enter your expression:')
        print(expr + '=')
        print(eval_math_expr(expr))
    else:
        for expr, res in {"2": 2, "2*4": 8, "4+8": 12, "100/3": 33, "2^3": 8}.items():
            result = eval_math_expr(expr)
            if res != result:
                print("Computing", expr, "got", result, "instead of", res)


I also took this chance to remove the silent catch of exceptions which makes things hard to understand when an error occurs.

By the way, bare except (with no explicit exception type caught) and ignored exceptions are usually frowned upon as they make debugging very painful.

Naming

Your function names are not very good. Your could define is_digit, get_number, perform_operation.

Simplify is_digit function

The best data structure for the check you want to perform is a set.
You could write something like:

DIGITS = set('0123456789')

def is_digit(var):
    return var in DIGITS


Simplify perform_operation

The best data structure for the logic you want to perform is a dictionnary mapping operators to functions.

import operator

OPERATIONS = {
    '+' : operator.add,
    '-' : operator.sub,
    '*' : operator.mul,
    '/' : operator.floordiv,
    '^' : operator.pow,
}


def perform_operation(string, num1, num2):
    op = OPERATIONS.get(string, None)
    if op is not None:
        return op(num1, num2)
    else:
        return None  # How to handle this?


Note: using floordiv instead of truediv fixes an issue found with tests mentionned above.

Simplify get_number

In Python, you usually don't need to get elements from an object by index. What you want is usually to iterate over the different elements. I highly recommand reading/watching Ned Batchelder talk called "Loop Like A Native". In your case, you could write something like:

def get_number(varstr):
    s = ""
    for c in varstr:
        if not is_digit(c):
            break
        s += c
    return (int(s), len(s))


Using tuple unpacking

As already said, you could use number1, end_number1 = get_number(expr) to call get_number only once.

At this stage, the code looks like:

import operator

DIGITS = set('0123456789')
OPERATIONS = {
    '+' : operator.add,
    '-' : operator.sub,
    '*' : operator.mul,
    '/' : operator.floordiv,
    '^' : operator.pow,
}


def is_digit(var):
    return var in DIGITS

def get_number(varstr):
    s = ""
    for c in varstr:
        if not is_digit(c):
            break
        s += c
    return (int(s), len(s))

def perform_operation(string, num1, num2):
    op = OPERATIONS.get(string, None)
    if op is not None:
        return op(num1, num2)
    else:
        return None  # How to handle this?

def eval_math_expr(expr):
    negate = False
    while True:
        try: 
            if expr[0] == '-': #for negative numbers
                negate = True #because here the numbers are string format
                expr = expr[1:]
            number1, end_number1 = get_number(expr)
            expr = expr[end_number1:]
            if negate == True:
                number1 = -number1
                negate = False
            if expr == '':
                return number1
            op = expr[0]
            expr = expr[1:]
            number2, end_number2 = get_number(expr)
            result = perform_operation(op, number1, number2)
            number1 = result
            expr = str(number1) + expr[end_number2:]
        except Exception as e:
            print(e)
            break
    return number1


if __name__ == '__main__':
    interactive = False
    if interactive:
        expr = input('Enter your expression:')
        print(expr + '=')
        print(eval_math_expr(expr))
    else:
        for expr, res in {"2": 2, "2*4": 8, "4+8": 12, "100/3": 33, "2^3": 8, "-2": -2, "-2-3": -5}.items():
            result = eval_math_expr(expr)
            if res != result:
                print("Computing", expr, "got", result, "instead of", res)


About negate

You set negate to False at the very beginning and make sure you reset it back to False at each iteration so that the next iteration works fine. It would be clearer to initialise it to False at the beginning of the iteration. You could also set it to negate = expr[0] == '-'.

        negate = expr[0] == '-' #for negative numbers
        if negate:
            expr = expr[1:]
        number1, end_number1 = get_number(expr)
        expr = expr[end_number1:]
        if negate:
            number1 *= -1


An alterative could be to handle this as part of the get_number function.

def get_number(varstr):
    s = ""
    if varstr[0] == '-':
        s += "-"
        varstr = varstr[1:]
    for c in varstr:
        if not is_digit(c):
            break
        s += c
    return (int(s), len(s))


def eval_math_expr(expr):
    while True:
        try: 
            number1, end_number1 = get_number(expr)
            expr = expr[end_number1:]
            if expr == '':
                return number1
            op = expr[0]
            expr = expr[1:]
            number2, end_number2 = get_number(expr)
            number1 = perform_operation(op, number1, number2)
            expr = str(number1) + expr[end_number2:]
        except Exception as e:
            print(e)
            break
    return number1


The whole code is:

import operator

DIGITS = set('0123456789')
OPERATIONS = {
    '+' : operator.add,
    '-' : operator.sub,
    '*' : operator.mul,
    '/' : operator.floordiv,
    '^' : operator.pow,
}


def is_digit(var):
    return var in DIGITS

def get_number(varstr):
    s = ""
    if varstr[0] == '-':
        s += "-"
        varstr = varstr[1:]
    for c in varstr:
        if not is_digit(c):
            break
        s += c
    return (int(s), len(s))

def perform_operation(string, num1, num2):
    op = OPERATIONS.get(string, None)
    if op is not None:
        return op(num1, num2)
    else:
        return None  # How to handle this?

def eval_math_expr(expr):
    while True:
        try: 
            number1, end_number1 = get_number(expr)
            expr = expr[end_number1:]
            if expr == '':
                return number1
            op = expr[0]
            expr = expr[1:]
            number2, end_number2 = get_number(expr)
            number1 = perform_operation(op, number1, number2)
            expr = str(number1) + expr[end_number2:]
        except Exception as e:
            print(e)
            break
    return number1


if __name__ == '__main__':
    interactive = False
    if interactive:
        expr = input('Enter your expression:')
        print(expr + '=')
        print(eval_math_expr(expr))
    else:
        for expr, res in {"2": 2, "2*4": 8, "4+8": 12, "100/3": 33, "2^3": 8, "-2": -2, "-2-3": -5}.items():
            result = eval_math_expr(expr)
            if res != result:
                print("Computing", expr, "got", result, "instead of", res)


Reorganisation in the main function

At the moment, the code parses two number and an operation, computes the operation, convert the result into a string to be parsed at next iteration. Thus, each temporary result gets converted from integer to string then integer. This leads to issues when the result cannot be converted back and forth - for instance when you perform divisions with non-integer result. Also this could be optimised a lot with the following logic: 


get the first number and store it as the current number
in each iteration: get the operation and the next number. Compute the operation and store the result as the current number, to be reused at next iteration.
at the end, return the current number.


You could write this:

def eval_math_expr(expr):
    n, end_n = get_number(expr)
    expr = expr[end_n:]
    while expr:
        op, expr = expr[0], expr[1:]
        n2, end_n = get_number(expr)
        n = perform_operation(op, n, n2)
        expr = expr[end_n:]
    return n