from datetime import datetime, timedelta

from invisibleroads_macros_security import make_random_string


class DictionarySafe(dict):

    def __init__(self, key_length):
        self.key_length = key_length

    def put(self, value, time_in_seconds=None):
        while True:
            key = make_random_string(self.key_length)
            try:
                self[key]
            except KeyError:
                break
        self.set(key, value, time_in_seconds)
        return key

    def set(self, key, value, time_in_seconds=None):
        self[key] = value, get_expiration_datetime(time_in_seconds)

    def get(self, key):
        value, expiration_datetime = self[key]
        if datetime.now() > expiration_datetime:
            del self[key]
            raise KeyError
        return value


def get_expiration_datetime(time_in_seconds):
    if not time_in_seconds:
        return
    return datetime.now() + timedelta(seconds=time_in_seconds)


def evaluate_expression(expression_string, value_by_name):
    # https://realpython.com/python-eval-function
    code = compile(expression_string, '<string>', 'eval')
    for name in code.co_names:
        if name not in value_by_name:
            raise NameError(f'{name} not defined')
    return eval(code, {'__builtins__': {}}, value_by_name)

from datetime import datetime, timedelta

from invisibleroads_macros_security import make_random_string


class DictionarySafe(dict):

    def __init__(self, key_length):
        self.key_length = key_length

    def put(self, value, time_in_seconds=None):
        while True:
            key = make_random_string(self.key_length)
            try:
                self[key]
            except KeyError:
                break
        self.set(key, value, time_in_seconds)
        return key

    def set(self, key, value, time_in_seconds=None):
        self[key] = value, get_expiration_datetime(time_in_seconds)

    def get(self, key):
        value, expiration_datetime = self[key]
        if expiration_datetime and datetime.now() > expiration_datetime:
            del self[key]
            raise KeyError
        return value


def get_expiration_datetime(time_in_seconds):
    if not time_in_seconds:
        return
    return datetime.now() + timedelta(seconds=time_in_seconds)


def evaluate_expression(expression_string, value_by_name):
    # https://realpython.com/python-eval-function
    code = compile(expression_string, '<string>', 'eval')
    for name in code.co_names:
        if name not in value_by_name:
            raise NameError(f'{name} not defined')
    return eval(code, {'__builtins__': {}}, value_by_name)

Support case when expiration_datetime is None
Support case when expiration_datetime is None

  from datetime import datetime, timedelta
  
  from invisibleroads_macros_security import make_random_string
  
  
  class DictionarySafe(dict):
  
      def __init__(self, key_length):
          self.key_length = key_length
  
      def put(self, value, time_in_seconds=None):
          while True:
              key = make_random_string(self.key_length)
              try:
                  self[key]
              except KeyError:
                  break
          self.set(key, value, time_in_seconds)
          return key
  
      def set(self, key, value, time_in_seconds=None):
          self[key] = value, get_expiration_datetime(time_in_seconds)
  
      def get(self, key):
          value, expiration_datetime = self[key]
-         if datetime.now() > expiration_datetime:
+         if expiration_datetime and datetime.now() > expiration_datetime:
              del self[key]
              raise KeyError
          return value
  
  
  def get_expiration_datetime(time_in_seconds):
      if not time_in_seconds:
          return
      return datetime.now() + timedelta(seconds=time_in_seconds)
  
  
  def evaluate_expression(expression_string, value_by_name):
      # https://realpython.com/python-eval-function
      code = compile(expression_string, '<string>', 'eval')
      for name in code.co_names:
          if name not in value_by_name:
              raise NameError(f'{name} not defined')
      return eval(code, {'__builtins__': {}}, value_by_name)
  
Support case when expiration_datetime is None
## Code Before:
from datetime import datetime, timedelta

from invisibleroads_macros_security import make_random_string


class DictionarySafe(dict):

    def __init__(self, key_length):
        self.key_length = key_length

    def put(self, value, time_in_seconds=None):
        while True:
            key = make_random_string(self.key_length)
            try:
                self[key]
            except KeyError:
                break
        self.set(key, value, time_in_seconds)
        return key

    def set(self, key, value, time_in_seconds=None):
        self[key] = value, get_expiration_datetime(time_in_seconds)

    def get(self, key):
        value, expiration_datetime = self[key]
        if datetime.now() > expiration_datetime:
            del self[key]
            raise KeyError
        return value


def get_expiration_datetime(time_in_seconds):
    if not time_in_seconds:
        return
    return datetime.now() + timedelta(seconds=time_in_seconds)


def evaluate_expression(expression_string, value_by_name):
    # https://realpython.com/python-eval-function
    code = compile(expression_string, '<string>', 'eval')
    for name in code.co_names:
        if name not in value_by_name:
            raise NameError(f'{name} not defined')
    return eval(code, {'__builtins__': {}}, value_by_name)

## Instruction:
Support case when expiration_datetime is None
## Code After:
from datetime import datetime, timedelta

from invisibleroads_macros_security import make_random_string


class DictionarySafe(dict):

    def __init__(self, key_length):
        self.key_length = key_length

    def put(self, value, time_in_seconds=None):
        while True:
            key = make_random_string(self.key_length)
            try:
                self[key]
            except KeyError:
                break
        self.set(key, value, time_in_seconds)
        return key

    def set(self, key, value, time_in_seconds=None):
        self[key] = value, get_expiration_datetime(time_in_seconds)

    def get(self, key):
        value, expiration_datetime = self[key]
        if expiration_datetime and datetime.now() > expiration_datetime:
            del self[key]
            raise KeyError
        return value


def get_expiration_datetime(time_in_seconds):
    if not time_in_seconds:
        return
    return datetime.now() + timedelta(seconds=time_in_seconds)


def evaluate_expression(expression_string, value_by_name):
    # https://realpython.com/python-eval-function
    code = compile(expression_string, '<string>', 'eval')
    for name in code.co_names:
        if name not in value_by_name:
            raise NameError(f'{name} not defined')
    return eval(code, {'__builtins__': {}}, value_by_name)
