def config(self, **kwargs: Union[int, str, bool]) -> None: """ Assign values to self.configuration dictionary. :raise ValueError: if type of a dictionary value is wrong. """ if "letter_sequence" in kwargs: type_check.type_guard(kwargs["letter_sequence"], str) self.configuration["letter_sequence"] = kwargs["letter_sequence"] if "key" in kwargs and kwargs["key"] is not None: type_check.type_guard(kwargs["key"], int) self.configuration["key"] = kwargs["key"] if "seed" in kwargs: type_check.type_guard(kwargs["seed"], int) self.configuration["seed"] = kwargs["seed"] if "shuffle" in kwargs: type_check.type_guard(kwargs["shuffle"], bool) self.configuration["shuffle"] = kwargs["shuffle"] if "decrypt" in kwargs: type_check.type_guard(kwargs["decrypt"], bool) self.configuration["decrypt"] = kwargs["decrypt"]
def _process_subroutines( self, configurations: Dict[str, KWARGS_TYPE], **kwargs: KWARGS_TYPE ) -> Dict[str, KWARGS_TYPE]: """ Extend functionality of encrypt and decrypt to accept route lists. :raise ValueError: if type of a dictionary value is wrong. """ # flags are all set to False. flag_route: bool = False flag_replace_route: bool = False # get route from kwargs if provided. if "route" in kwargs: route: List[int] = kwargs["route"] type_check.type_guard(route, list) flag_route = True # get replace route bool from kwargs if provided. if "replace_route" in kwargs: replace_route: bool = kwargs["replace_route"] type_check.type_guard(replace_route, bool) flag_replace_route = True # if there is a route list, proceed to next steps. if flag_route: if flag_replace_route: # save given route list in configuration dict. self.config(route=route) # replace route in configurations. configurations["route"] = route return configurations
def _config_subroutines(self, **kwargs: KWARGS_TYPE) -> None: """ Assign values to self.configuration dictionary. :raise ValueError: if type of a dictionary value is wrong. """ if "key" in kwargs and kwargs["key"] is not None: type_check.type_guard(kwargs["key"], int) self.configuration["key"] = kwargs["key"]
def find_unique_letters(text: str) -> Set[str]: """ Create a set of all letters in a string. :param text: string source. :return: a set of all unique letters in the string :rtype: set """ # check types type_check.type_guard(text, str) # create and return set return set(text)
def _process( self, text: str, key: Optional[int], replace_key: bool, decrypt: bool, **kwargs: KWARGS_TYPE, ) -> str: """ Handle the process for both encryption and decryption. This method main job is to fetch configurations and modify them if necessary and then feed it into the translator method. :param text : string to be processed. :param key : key for encryption/decryption. :param decrypt : switch for encryption/decryption. :param replace_key : replace the old key in self.configuration with new one. :return : encrypted/decrypted string. :rtype : str """ # explicitly switch mode to encryption/decryption. self.config(decrypt=decrypt) # type annotate. configuration: Dict[str, KWARGS_TYPE] # deep copy self.configuration dictionary into new dictionary to be used. configuration = {i: j for (i, j) in self.configuration.items()} if key: # check key type to be compatible. type_check.type_guard(key, int) # fetch configurations based on key and replace key states. if key and replace_key: # change key in self.configuration. self.config(key=key) # change the key in the copied dictionary, configuration["key"] = key elif key and not replace_key: # self.configuration key will remain unchanged. # change the key in the copied dictionary. configuration["key"] = key # do sub-process on configuration. configuration = self._process_subroutines(configuration, **kwargs) # return a call to cipher translator function with # configuration dictionary as arguments. return self._translator(text, **configuration)
def map_letters_to_indexes(text: str) -> Dict[str, List[int]]: """ Create an index mapping dictionary. Dictionary holds that maps every letter to a list of indexes of its occurrence in a given string :param text: string source for finding indexes letters in it. :return: a dictionary with letter as key and a list of indexes as value. :rtype: dict """ # check types type_check.type_guard(text, str) # return a dictionary with letter as key and a list of indexes as value return { letter: find_letter_indexes(text, letter) for letter in find_unique_letters(text) }
def find_letter_indexes(text: str, letter: str) -> List[int]: """ List indexes of a letter in a string. This function returns a list of index of every occurrence of a letter in a string. :param text: string source :param letter: the letter to find all of its occurrence indexes :return: list containing letters occurrence indexes :rtype: list """ # check types type_check.type_guard(text, str) type_check.type_guard(letter, str) # return index list return [ index for index, character in enumerate(text) if character == letter ]
def _process(self, text: str, key: Optional[int], replace_key: bool, decrypt: bool) -> str: """ Handle the process for both encryption and decryption. :param text : string to be processed. :param key : key for encryption/decryption. :param decrypt : switch for encryption/decryption. :param replace_key : replace the old key in self.configuration with new one. :return : encrypted/decrypted string. :rtype : str """ # explicitly switch mode to encryption/decryption. self.config(decrypt=decrypt) # replace the old key in self.configuration with new key if replace_key: self.config(key=key) if key and not replace_key: # deep copy self.configuration dictionary into new dictionary to be used. configuration = {i: j for (i, j) in self.configuration.items()} # check key type to be compatible. type_check.type_guard(key, int) # change the key in the copied dictionary, # self.configuration key will remain unchanged. configuration["key"] = key else: # alias self.configuration dictionary to configuration # in this case configuration dictionary isn't a copy # and is just a pointer to self.configuration configuration = self.configuration # return a call to affine_cipher_translator function with # string and unpacked (prepended "**" is used to unpack the dictionary) # configuration dictionary as arguments. return self._translator(text, **configuration)
def config(self, **kwargs: KWARGS_TYPE) -> None: """ Assign values to self.configuration dictionary. :raise ValueError: if type of a dictionary value is wrong. """ if "letter_sequence" in kwargs: type_check.type_guard(kwargs["letter_sequence"], str) self.configuration["letter_sequence"] = kwargs["letter_sequence"] if "seed" in kwargs: type_check.type_guard(kwargs["seed"], int) self.configuration["seed"] = kwargs["seed"] if "shuffle" in kwargs: type_check.type_guard(kwargs["shuffle"], bool) self.configuration["shuffle"] = kwargs["shuffle"] if "decrypt" in kwargs: type_check.type_guard(kwargs["decrypt"], bool) self.configuration["decrypt"] = kwargs["decrypt"] # do subroutines. self._config_subroutines(**kwargs)
def test_different_types(self): with self.assertRaises(TypeError): type_guard("Hi this is a str type", int) with self.assertRaises(TypeError): type_guard(2, float)