myth-hash 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Copyright 2024 Claas Flint
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ include myth-hash/data/*.json
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.1
2
+ Name: myth-hash
3
+ Version: 0.1.0
4
+ Summary: A tool for generating human-readable, multilingual fantasy character names based on input string hashes.
5
+ Home-page: https://github.com/yourusername/myth_hash
6
+ Author: Claas Flint
7
+ Author-email: claas.flint@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+
16
+ # Myth Hash
17
+
18
+ `Myth Hash` is aPython package designed to create human-readable hashes that are not only functional but also cognitively engaging. A key feature of this tool is its multilingual support, enabling the generation of hashes in multiple languages. The concept behind these human-readable hashes is to enhance communication and recognition by creating hashes that form vivid, memorable mental images. The goal is to make the hash as human-compatible as possible, ensuring it effectively serves its purpose in an intuitive and user-friendly manner.
19
+ ## Features
20
+
21
+ - **Character Name Generation:** Generates a unique fantasy name based on an input string.
22
+ - **Supported Languages:** Currently supports English (`en`) and German (`de`).
23
+ - **CLI Support:** Easily generate names via the command line.
24
+ - **Library Usage:** Integrate `Myth Hash` into your Python projects.
25
+ - **Customizable Data:** Modify the included JSON files to customize the generated names.
26
+
27
+ ## Installation
28
+
29
+ You can install `Myth Hash` using pip:
30
+
31
+ ```bash
32
+ pip install myth-hash
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ### Command Line Interface (CLI)
38
+
39
+ After installing the package, you can use the myth-hash command to generate fantasy names.
40
+
41
+ Basic Example:
42
+
43
+ ```bash
44
+ myth-hash "The moon whispered secrets, but only the cats understood."
45
+ ```
46
+ Example Output:
47
+ ```bash
48
+ exotic-thoughtful-Griffin
49
+ ```
50
+
51
+ ### Using as a Library
52
+
53
+ You can also use Myth Hash within your Python code:
54
+
55
+ Example:
56
+ ```python
57
+ import myth_hash
58
+
59
+ # Generate a character name using the hash_name function
60
+ name_parts = myth_hash.hash_name("The moon whispered secrets, but only the cats understood.", "en")
61
+ print(name_parts) # Output: ('exotic', 'thoughtful', 'Griffin')
62
+
63
+ # Access individual parts of the name
64
+ physical_attr, personality_attr, mystical_creature = name_parts
65
+ print(f"Physical Attribute: {physical_attr}")
66
+ print(f"Personality Trait: {personality_attr}")
67
+ print(f"Mystical Creature: {mystical_creature}")
68
+ ```
69
+
70
+ ## Contributing
71
+
72
+ Contributions are welcome! Please fork the repository, create a new branch for your feature or bug fix, and submit a pull request.
73
+
74
+ ## License
75
+
76
+ This project is licensed under the MIT License. See the LICENSE file for details.
@@ -0,0 +1,61 @@
1
+ # Myth Hash
2
+
3
+ `Myth Hash` is aPython package designed to create human-readable hashes that are not only functional but also cognitively engaging. A key feature of this tool is its multilingual support, enabling the generation of hashes in multiple languages. The concept behind these human-readable hashes is to enhance communication and recognition by creating hashes that form vivid, memorable mental images. The goal is to make the hash as human-compatible as possible, ensuring it effectively serves its purpose in an intuitive and user-friendly manner.
4
+ ## Features
5
+
6
+ - **Character Name Generation:** Generates a unique fantasy name based on an input string.
7
+ - **Supported Languages:** Currently supports English (`en`) and German (`de`).
8
+ - **CLI Support:** Easily generate names via the command line.
9
+ - **Library Usage:** Integrate `Myth Hash` into your Python projects.
10
+ - **Customizable Data:** Modify the included JSON files to customize the generated names.
11
+
12
+ ## Installation
13
+
14
+ You can install `Myth Hash` using pip:
15
+
16
+ ```bash
17
+ pip install myth-hash
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ### Command Line Interface (CLI)
23
+
24
+ After installing the package, you can use the myth-hash command to generate fantasy names.
25
+
26
+ Basic Example:
27
+
28
+ ```bash
29
+ myth-hash "The moon whispered secrets, but only the cats understood."
30
+ ```
31
+ Example Output:
32
+ ```bash
33
+ exotic-thoughtful-Griffin
34
+ ```
35
+
36
+ ### Using as a Library
37
+
38
+ You can also use Myth Hash within your Python code:
39
+
40
+ Example:
41
+ ```python
42
+ import myth_hash
43
+
44
+ # Generate a character name using the hash_name function
45
+ name_parts = myth_hash.hash_name("The moon whispered secrets, but only the cats understood.", "en")
46
+ print(name_parts) # Output: ('exotic', 'thoughtful', 'Griffin')
47
+
48
+ # Access individual parts of the name
49
+ physical_attr, personality_attr, mystical_creature = name_parts
50
+ print(f"Physical Attribute: {physical_attr}")
51
+ print(f"Personality Trait: {personality_attr}")
52
+ print(f"Mystical Creature: {mystical_creature}")
53
+ ```
54
+
55
+ ## Contributing
56
+
57
+ Contributions are welcome! Please fork the repository, create a new branch for your feature or bug fix, and submit a pull request.
58
+
59
+ ## License
60
+
61
+ This project is licensed under the MIT License. See the LICENSE file for details.
@@ -0,0 +1,5 @@
1
+ from .core import hash_name
2
+
3
+ __all__ = [
4
+ "hash_name",
5
+ ]
@@ -0,0 +1,89 @@
1
+ import argparse
2
+ import json
3
+ import logging
4
+
5
+ from myth_hash import hash_name
6
+
7
+
8
+ def setup_logging(log_level: str) -> None:
9
+ numeric_level = getattr(logging, log_level.upper(), None)
10
+ if not isinstance(numeric_level, int):
11
+ raise ValueError(f"Invalid log level: {log_level}")
12
+ logging.basicConfig(level=numeric_level)
13
+
14
+
15
+ def hash_name_cli(input_string: str, language: str, output_format: str) -> None:
16
+ try:
17
+ physical_attr, personality_attr, character = hash_name(input_string, language)
18
+ except Exception as e:
19
+ logging.error(f"Failed to generate fantasy name: {e}")
20
+ raise
21
+
22
+ if output_format == "text":
23
+ print(f"{physical_attr}-{personality_attr}-{character.replace(' ', '')}")
24
+ elif output_format == "json":
25
+ output = {
26
+ "physical_attribute": physical_attr,
27
+ "personality_attribute": personality_attr,
28
+ "character": character,
29
+ }
30
+ print(json.dumps(output, ensure_ascii=False))
31
+
32
+
33
+ def parse_arguments() -> argparse.Namespace:
34
+ parser = argparse.ArgumentParser(
35
+ description="Generates a fantasy name consisting of two character attributes and a mythical creature from a hash value of an input string."
36
+ )
37
+ parser.add_argument(
38
+ "input_string",
39
+ type=str,
40
+ help="Input string to hash and generate a fantasy name.",
41
+ )
42
+ parser.add_argument(
43
+ "-l",
44
+ "--language",
45
+ type=str,
46
+ default="en",
47
+ choices=["en", "de"],
48
+ help="Specify the output language. Supported languages: English (en) and German (de). Default is English.",
49
+ )
50
+ parser.add_argument(
51
+ "-f",
52
+ "--format",
53
+ type=str,
54
+ default="text",
55
+ choices=["text", "json"],
56
+ help="Specify the output format. Choose between plain text (text) and JSON (json). Default is text.",
57
+ )
58
+ parser.add_argument(
59
+ "--log-level",
60
+ type=str,
61
+ default="INFO",
62
+ choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
63
+ help="Set the logging level. Default is INFO.",
64
+ )
65
+
66
+ return parser.parse_args()
67
+
68
+
69
+ def validate_input_string(input_string: str) -> None:
70
+ if not input_string.strip():
71
+ raise ValueError("Input string cannot be empty.")
72
+
73
+
74
+ def main():
75
+ args = parse_arguments()
76
+
77
+ setup_logging(args.log_level)
78
+
79
+ try:
80
+ validate_input_string(args.input_string)
81
+ hash_name_cli(args.input_string, args.language, args.format)
82
+ except ValueError as ve:
83
+ logging.error(f"Input validation error: {ve}")
84
+ except Exception as e:
85
+ logging.error(f"An unexpected error occurred: {e}")
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()
@@ -0,0 +1,13 @@
1
+ from .character_data_loader import CharacterData, CharacterDataLoader
2
+ from .hash_util import check_language, generate_indices, hash_name
3
+ from .words import CharacterNoun, NominativAdjective
4
+
5
+ __all__ = [
6
+ "CharacterDataLoader",
7
+ "CharacterData",
8
+ "hash_name",
9
+ "generate_indices",
10
+ "check_language",
11
+ "CharacterNoun",
12
+ "NominativAdjective",
13
+ ]
@@ -0,0 +1,70 @@
1
+ import json
2
+ from dataclasses import dataclass
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ from .words import CharacterNoun, NominativAdjective
7
+
8
+ BASE_PATH = Path(__file__).parent.parent / "data"
9
+ CHARACTER_NOUNS_FILE = BASE_PATH / "character_nouns.json"
10
+ PHYSICAL_ATTRIBUTES_FILE = BASE_PATH / "physical_attributes.json"
11
+ PERSONALITY_ATTRIBUTES_FILE = BASE_PATH / "personality_attributes.json"
12
+
13
+
14
+ @dataclass
15
+ class CharacterData:
16
+ character_nouns: list[CharacterNoun]
17
+ physical_attributes: list[NominativAdjective]
18
+ personality_attributes: list[NominativAdjective]
19
+
20
+
21
+ class CharacterDataLoader:
22
+ _instance: Optional["CharacterDataLoader"] = None
23
+ _character_data: CharacterData | None = None
24
+
25
+ def __new__(cls):
26
+ if cls._instance is None:
27
+ cls._instance = super().__new__(cls)
28
+ cls._instance._load_data()
29
+ return cls._instance
30
+
31
+ def _load_data(self) -> None:
32
+ self._character_data = CharacterData(
33
+ character_nouns=self._load_character_nouns(),
34
+ physical_attributes=self._load_attributes(PHYSICAL_ATTRIBUTES_FILE),
35
+ personality_attributes=self._load_attributes(PERSONALITY_ATTRIBUTES_FILE),
36
+ )
37
+
38
+ @staticmethod
39
+ def _load_attributes(file_path: Path) -> list[NominativAdjective]:
40
+ try:
41
+ with open(file_path, encoding="utf8") as f:
42
+ json_dict = json.load(f)
43
+ except (FileNotFoundError, json.JSONDecodeError) as e:
44
+ raise RuntimeError(f"Error loading attributes from {file_path}: {e}") from e
45
+
46
+ return [
47
+ NominativAdjective(word_id, data["words"])
48
+ for word_id, data in json_dict.items()
49
+ ]
50
+
51
+ @staticmethod
52
+ def _load_character_nouns() -> list[CharacterNoun]:
53
+ try:
54
+ with open(CHARACTER_NOUNS_FILE, encoding="utf8") as f:
55
+ json_dict = json.load(f)
56
+ except (FileNotFoundError, json.JSONDecodeError) as e:
57
+ raise RuntimeError(
58
+ f"Error loading character nouns from {CHARACTER_NOUNS_FILE}: {e}"
59
+ ) from e
60
+
61
+ return [
62
+ CharacterNoun(char_id, data["data"]) for char_id, data in json_dict.items()
63
+ ]
64
+
65
+ @property
66
+ def character_data(self) -> CharacterData:
67
+ if self._character_data is None:
68
+ self._load_data()
69
+ assert self._character_data is not None
70
+ return self._character_data
@@ -0,0 +1,63 @@
1
+ import hashlib
2
+
3
+ from .character_data_loader import CharacterData, CharacterDataLoader
4
+
5
+ SUPPORTED_LANGUAGES = {"en", "de"}
6
+
7
+
8
+ def check_language(language: str) -> None:
9
+ if language not in SUPPORTED_LANGUAGES:
10
+ raise ValueError(
11
+ f"Unsupported language '{language}'. Supported languages are: {', '.join(SUPPORTED_LANGUAGES)}."
12
+ )
13
+
14
+
15
+ def generate_indices(input_string: str, list_sizes: list[int]) -> list[int]:
16
+ sha256 = hashlib.sha256(input_string.encode())
17
+ d = sha256.digest()
18
+ hash_length = len(d)
19
+ indices = []
20
+
21
+ for i, size in enumerate(list_sizes):
22
+ start = (hash_length // len(list_sizes)) * i
23
+ end = start + (hash_length // len(list_sizes))
24
+
25
+ segment = int.from_bytes(d[start:end], "big")
26
+
27
+ index = segment % size
28
+ indices.append(index)
29
+
30
+ return indices
31
+
32
+
33
+ def hash_name(input_string: str, language: str = "en") -> tuple[str, str, str]:
34
+ check_language(language)
35
+
36
+ data_loader = CharacterDataLoader()
37
+ character_data: CharacterData = data_loader.character_data
38
+
39
+ indices = generate_indices(
40
+ input_string,
41
+ [
42
+ len(character_data.physical_attributes),
43
+ len(character_data.personality_attributes),
44
+ len(character_data.character_nouns),
45
+ ],
46
+ )
47
+
48
+ physical_attr_index = indices[0]
49
+ personality_attr_index = indices[1]
50
+ character_nouns_index = indices[2]
51
+
52
+ character_noun = character_data.character_nouns[character_nouns_index]
53
+ physical_attr = character_data.physical_attributes[physical_attr_index].word(
54
+ language, character_noun.get_attribute(language, "gender")
55
+ )
56
+ personality_attr = character_data.personality_attributes[
57
+ personality_attr_index
58
+ ].word(language, character_noun.get_attribute(language, "gender"))
59
+ return (
60
+ physical_attr,
61
+ personality_attr,
62
+ character_noun.get_attribute(language, "word"),
63
+ )
@@ -0,0 +1,117 @@
1
+ class NominativAdjective:
2
+ def __init__(self, word_id: int, words: dict) -> None:
3
+ """
4
+ Constructor for the NominativAdjective class.
5
+
6
+ :param word_id: ID of the adjective
7
+ :param words: A dictionary that contains the words in different languages and genders
8
+ """
9
+ self.word_id = word_id
10
+ self.validate_words(words)
11
+ self.words = words
12
+
13
+ def __str__(self) -> str:
14
+ return str(
15
+ self.words.get("en", {}).get("neutral", "No neutral English word available")
16
+ )
17
+
18
+ def __repr__(self) -> str:
19
+ return f"Adjective: {self.word_id} - {self.words}"
20
+
21
+ @staticmethod
22
+ def validate_words(words: dict):
23
+ """
24
+ Validates the structure of the words dictionary. The dictionary should contain languages as keys
25
+ and dictionaries as values, with the inner dictionaries having genders as keys and words as values.
26
+
27
+ :param words: Dictionary to validate
28
+ :raises ValueError: If the dictionary structure is invalid
29
+ """
30
+ for language, genders in words.items():
31
+ if not isinstance(genders, dict):
32
+ raise ValueError(
33
+ f"The value for language '{language}' must be a dictionary."
34
+ )
35
+ for gender in genders:
36
+ if gender not in ["masculine", "feminine", "neutral"]:
37
+ raise ValueError(
38
+ f"Invalid gender '{gender}' for language '{language}'."
39
+ )
40
+
41
+ def word(self, language: str, gender: str = "neutral") -> str:
42
+ """
43
+ Returns the word in the specified language and gender.
44
+
45
+ :param language: The language of the word
46
+ :param gender: The gender of the word. Options are "masculine", "feminine", and "neutral".
47
+ If no gender is specified, the neutral word is returned.
48
+ :return: The word in the specified language and gender
49
+ """
50
+ try:
51
+ return str(self.words[language][gender])
52
+ except KeyError as exc:
53
+ raise ValueError(
54
+ f"No word found for the language '{language}' and gender '{gender}'."
55
+ ) from exc
56
+
57
+ def set_word(self, language: str, gender: str, word: str) -> None:
58
+ self.words[language][gender] = word
59
+
60
+ def as_json(self) -> dict:
61
+ """
62
+ Returns a JSON-compatible dictionary that contains the attributes of the adjective.
63
+
64
+ :return: A dictionary that contains the attributes of the adjective
65
+ """
66
+ return {"id": self.word_id, "words": self.words}
67
+
68
+
69
+ class CharacterNoun:
70
+ def __init__(self, character_id: int, data: dict) -> None:
71
+ """
72
+ Constructor for the CharacterNoun class.
73
+
74
+ :param character_id: ID of the noun
75
+ :param data: A dictionary containing the word and gender information in different languages
76
+ """
77
+ self.character_id = character_id
78
+ self.data = data
79
+
80
+ def __str__(self) -> str:
81
+ return self.get_attribute("en", "word")
82
+
83
+ def __repr__(self) -> str:
84
+ return f"CharacterNoun: {self.character_id} - {self.data}"
85
+
86
+ def get_attribute(self, language: str, attribute: str) -> str:
87
+ """
88
+ Returns the specified attribute in the specified language.
89
+
90
+ :param language: The language of the attribute
91
+ :param attribute: The attribute to get. Options are 'word' and 'gender'.
92
+ :return: The attribute in the specified language
93
+ """
94
+ try:
95
+ return str(self.data[language][attribute])
96
+ except KeyError as exc:
97
+ raise ValueError(
98
+ f"No {attribute} found for the language '{language}'."
99
+ ) from exc
100
+
101
+ def as_json(self) -> dict:
102
+ """
103
+ Returns a JSON-compatible dictionary that contains the attributes of the noun.
104
+
105
+ :return: A dictionary that contains the attributes of the noun
106
+ """
107
+ return {"character_id": self.character_id, "data": self.data}
108
+
109
+ @staticmethod
110
+ def from_json(json: dict) -> "CharacterNoun":
111
+ """
112
+ Returns a CharacterNoun object from a JSON-compatible dictionary.
113
+
114
+ :param json: A JSON-compatible dictionary that contains the attributes of the noun
115
+ :return: A CharacterNoun object
116
+ """
117
+ return CharacterNoun(json["character_id"], json["data"])