"""This module contains the Rot class.""" from CharacterCipher import CharacterCipher from KeyedCipher import KeyedCipher from Tools import LEN_ALPHABET, apply_caseless, offset, from_offset class Rot(CharacterCipher, KeyedCipher): """This is the cipher used in Rot7, Rot13, Ceasar's code, etc. This is a simple cipher that simply rotates the characters by an offset. For instance, ab..z -> b..za. The following attributes are used: key - What is the offset used in the rotation? This will initially be set to defaultKey. keyDescription - This is a description of what constitutes a valid key. defaultKey - This is the default key. """ keyDescription = "an integer in the range [0, 25]" defaultKey = 0 def __init__(self): """Set key to defaultKey.""" self.key = self.defaultKey def encryptCharacter(self, c): """Encrypt and return c.""" return self._encryptCharacter(c, self.key) def decryptCharacter(self, c): """Decrypt and return c.""" return self._encryptCharacter(c, -self.key) def _encryptCharacter(self, c, key): """Encrypt and return c. Use the given key.""" if not c.isalpha(): return c return apply_caseless( lambda c: from_offset((offset(c) + key) % LEN_ALPHABET), c) def isValidKey(self, key): """Is key a a valid key? See keyDescription.""" return isinstance(key, type(0)) and 0 <= key and key < LEN_ALPHABET def __setattr__(self, attribute, value): """Extend KeyedCipher.__setattr__ to cast the key to an int.""" strvalue = value if attribute == "key": try: value = int(value) except ValueError: self.raiseInvalidKey(strvalue) KeyedCipher.__setattr__(self, attribute, value) # Do some testing. if __name__ == '__main__': cipher = Rot() assert cipher.isValidKey(0) assert not cipher.isValidKey(-1) assert not cipher.isValidKey("a") cipher.key = "1" assert cipher.key == 1 assert cipher.encrypt("aAzZ") == "bBaA" assert cipher.decrypt("bBaA") == "aAzZ"