update tests and docs

This commit is contained in:
InspectorCaracal 2022-07-27 13:24:32 -06:00
parent a620fae4c0
commit 32d518c7b9
3 changed files with 109 additions and 10 deletions

View file

@ -128,15 +128,80 @@ put a dictionary of custom name rules into `settings.py`
Generating a fantasy name takes the ruleset key as the "style" keyword, and can
return either a single name or multiple names. By default, it will return a
single name in the built-in "harsh" style.
single name in the built-in "harsh" style. The contrib also comes with "fluid" and "alien" styles.
```py
>>> namegen.fantasy_name()
'Vhon'
>>> namegen.fantasy_name(num=3, style="harsh")
['Kha', 'Kizdhu', 'Godögäk']
>>> namegen.fantasy_name(num=3, style="fluid")
['Aewalisash', 'Ayi', 'Iaa']
>>> namegen.fantasy_name(num=5, style="alien")
["Qz'vko'", "Xv'w'hk'hxyxyz", "Wxqv'hv'k", "Wh'k", "Xbx'qk'vz"]
```
### Multi-Word Fantasy Names
The `fantasy_name` function will only generate one name-word at a time, so for multi-word names
you'll need to combine pieces together. Depending on what kind of end result you want, there are
several approaches.
#### The simple approach
If all you need is for it to have multiple parts, you can generate multiple names at once and `join` them.
```py
>>> name = " ".join(namegen.fantasy_name(num=2)
>>> print(name)
Dezhvözh Khäk
```
If you want a little more variation between first/last names, you can also generate names for
different styles and then combine them.
```py
>>> name = "{first} {last}".format( first=namegen.fantasy_name(style="fluid"), last=namegen.fantasy_name(style="harsh") )
>>> print(name)
Ofasa Käkudhu
```
#### "Nakku Silversmith"
One common fantasy name practice is profession- or title-based surnames. To achieve this effect,
you can use the `last_name` function with a custom list of last names and combine it with your generated
fantasy name.
Example:
```py
NAMEGEN_LAST_NAMES = [ "Silversmith", "the Traveller", "Destroyer of Worlds" ]
NAMEGEN_REPLACE_LISTS = True
>>> name = "{first} {last}".format( first=namegen.fantasy_name(), last=namegen.last_name() )
>>> print(name)
Tözhkheko the Traveller
```
#### Elarion d'Yrinea, Thror Obinson
Another common flavor of fantasy names is to use a surname suffix or prefix. For that, you'll
need to add in the extra bit yourself.
Examples:
```py
>>> names = namegen.fantasy_name(num=2)
>>> name = f"{names[0]} za'{names[1]}"
>>> print(name)
Tithe za'Dhudozkok
>>> names = namegen.fantasy_name(num=2)
>>> name = f"{names[0]} {names[1]}son"
>>> print(name)
Kön Ködhöddoson
```
### Custom Fantasy Name style rules
The style rules are contained in a dictionary of dictionaries, where the style name
@ -187,12 +252,16 @@ a consonant. You can add on additional consonants which can only occur at the be
or end of a syllable, or you can add extra copies of already-defined consonants to
increase the frequency of them at the start/end of syllables.
For example, in the `example_style` above, we have a `start` of m, and `end` of x and n.
Taken with the rest of the consonants/vowels, this means you can have the syllables of `mez`
but not `zem`, and you can have `phex` or `phen` but not `xeph` or `neph`.
They can be left out of custom rulesets entirely.
#### vowels
Works exactly like consonants, but is instead used for the vowel selection. Single-
or multi-character strings are equally fine, and you can increase the frequency of
any given vowel by putting it into the list multiple times.
Vowels is a simple list of vowel phonemes - exactly like consonants, but instead used for the
vowel selection. Single-or multi-character strings are equally fine. It uses the same naive weighting system
as consonants - you can increase the frequency of any given vowel by putting it into the list multiple times.
#### length
A tuple with the minimum and maximum number of syllables a name can have.

View file

@ -71,8 +71,6 @@ import re
from os import path
from django.conf import settings
from evennia.utils.utils import is_iter
# Load name data from Behind the Name lists
dirpath = path.dirname(path.abspath(__file__))
_FIRSTNAME_LIST = []
@ -157,9 +155,9 @@ def fantasy_name(num=1, style="harsh", return_list=False):
if len(set):
raise KeyError(f"Style dictionary {style_name} is missing required keys: {' '.join(missing_keys)}")
if not (is_iter(style_dict['consonants']) and is_iter(style_dict['vowels'])):
raise ValueError(f"'consonants' and 'vowels' keys for style {style_name} must have iterable values.")
if not (type(style_dict['consonants']) is list and type(style_dict['vowels']) is list):
raise TypeError(f"'consonants' and 'vowels' for style {style_name} must be lists.")
if not (is_iter(style_dict['length']) and len(style_dict['length']) == 2):
raise ValueError(f"'length' key for {style_name} must have a minimum and maximum number of syllables.")

View file

@ -6,6 +6,26 @@ Tests for the Random Name Generator
from evennia.utils.test_resources import BaseEvenniaTest
from . import namegen
_INVALID_STYLES = {
"missing_keys": {
"consonants": ['c','d'],
"length": (1,2),
},
"invalid_vowels": {
"syllable": "CVC",
"consonants": ['c','d'],
"vowels": "aeiou",
"length": (1,2),
},
"invalid_length": {
"syllable": "CVC",
"consonants": ['c','d'],
"vowels": ['a','e'],
"length": 2,
},
}
namegen._FANTASY_NAME_STRUCTURES |= _INVALID_STYLES
class TestNameGenerator(BaseEvenniaTest):
def test_fantasy_name(self):
@ -38,7 +58,19 @@ class TestNameGenerator(BaseEvenniaTest):
with self.assertRaises(ValueError):
namegen.fantasy_name(style="dummy")
def test_structure_validation(self):
"""
Verify that validation raises the correct errors for invalid inputs.
"""
with self.assertRaises(KeyError):
namegen.fantasy_name(style="missing_keys")
with self.assertRaises(TypeError):
namegen.fantasy_name(style="invalid_vowels")
with self.assertRaises(ValueError):
namegen.fantasy_name(style="invalid_length")
def test_first_name(self):
"""