docs, modifying cli to use language packs

This commit is contained in:
evilchili 2023-11-24 10:01:33 -05:00
parent 45f4d6e401
commit 68128b5dc6
4 changed files with 162 additions and 34 deletions

112
README.md
View File

@ -1,11 +1,84 @@
# D&D Language Generator # D&D Name and Language Generator
This package is a fantasy language generator. By defining a number of characteristics about your imagined language -- the graphemes, their relative frequency distributions, the construction of syllables, and so on -- you can generate random but internally consistent gibberish that feels distinct, evocative, and appropriate to your setting. This package is a fantasy language generator. By defining a number of characteristics about your imagined language -- the graphemes, their relative frequency distributions, the construction of syllables, and so on -- you can generate random but internally consistent gibberish that feels distinct, evocative, and appropriate to your setting.
## Quick Start ## Usage
The `fanlang` command-line utility supports three commands:
* **names**: generate names
* **text**: generate a paragraph of text
* **list**: list the supported language in the current language pack
### Examples:
``` ```
>>> from language imported supported_languages % fanlang --language=dwarvish names --count 5
Tiny Châ Pothesadottyr
Khâkhu Zhûdothir
Quiet Ke Vêdothir
Cû Tozhon
Big Pâ Thadottyr
```
```
% fanlang --language=dwarvish text
Cû ne do tho khâ tasha, vê wûva lû, ku phu thâ thê, tûko kê, pevo kâ têtetv zha
pataso keks khate? Fâ zhû shû yf pho pa me. Dupha dê thê khâ! Shikm tu! Cê
sâdêto. Dê yo nâ topho, my sû pida phe, vi phûtw châcho, po sotê?
```
```
% fanlang list
Abyssal
Celestial
Common
Draconic
Dwarvish
Elvish
Gnomish
Halfling
Infernal
Lizardfolk
Orcish
Undercommon
```
## Language Packs
A *Language Pack* is a python package that defines one or more language modules. The default language pack includes a number of D&D languages with rules built according to the conventions established by my D&D group over several years of play in our homebrew setting.
The default language pack is [language.languages](language/languages/); each submodule contains a README that describes the basic characteristics of the language, along with examples.
### Using Your Own Language Pack
You can override `fanlang`'s default language pack by specifying the `FANLANG_LANGUAGE_PACK` environment variable:
```
# Create your ancient_elvish module in campaign/language_pack/ancient_elvish
% FANLANG_LANGUAGE_PACK=campaign.language_pack fanlang list
Ancient Elvish
``
### Setting the Default Language
'common' is the default language module. You can override this by setting the `FANLANG_DEFAULT_LANGUAGE` environment variable:
```
% FANLANG_DEFAULT_LANGUAGE=gnomish fanlang names --count=1
Jey Lea
```
You can read about creating custom language packs below.
## Library Quick Start
You can load all supported languages in a language pack using `language.load_langauge_pack()`:
```
>>> import language
>>> language_pack, supported_languages = language.load_language_pack()
>>> common = supported_languages['common'] >>> common = supported_languages['common']
>>> common.word(2) >>> common.word(2)
['apsoo', 'nirtoet'] ['apsoo', 'nirtoet']
@ -21,23 +94,38 @@ Proitsiiiy be itkif eesof detytaen. Ojaot tyskuaz apsoo nirtoet prenao.
} }
``` ```
## Supported Languages You can also load individual languages directly:
A number of D&D languages are defined, with rules built according to the ```
conventions established by my D&D group over several years of play in our >>> from language.languages import common
homebrew setting. You can find all supported languages [in the languages >>> common.Language.word(2)
submodule](language/languages/); each submodule contains a README that ['apsoo', 'nirtoet']
describes the basic characteristics of the language, along with examples. >>> str(common.Name)
"Quiet" Gushi Murk Lirpusome
```
## Defining a Language ## Defining a New Language Pack
### Layout Language packs are python packages with the following structure:
```
language_pack:
__init__.py
language_name:
__init__.py
base.py
names.py
rules.py
...
```
### Languge Modules
A language consists of several submodules: A language consists of several submodules:
* `base.py`, which contains grapheme definitions and the `Language` subclasses; * `base.py`, which contains grapheme definitions and the `Language` subclasses;
* `names.py`, which defines the `NameGenerator` subclasses; and * `names.py`, which defines the `NameGenerator` subclasses; and
* `rules.py`, which is optional but defines the rules all words in the language must follow. * `rules.py`, which is optional, and defines the rules all words in the language must follow.
### Language Construction ### Language Construction

View File

@ -1 +1,26 @@
from .languages import supported_languages import importlib
import os
import pkgutil
import sys
from types import ModuleType
language_pack = None
supported_languages = None
def _import_submodules(module):
pkgs = pkgutil.iter_modules(module.__path__)
for loader, module_name, is_pkg in pkgs:
yield importlib.import_module(f"{module.__name__}.{module_name}")
def load_language_pack(module_name: str = "") -> ModuleType:
if not module_name:
module_name = os.getenv("FANLANG_LANGUAGE_PACK", "language.languages")
language_pack = importlib.import_module(module_name)
_import_submodules(language_pack)
supported_languages = dict(
(module.__name__.split(".")[-1], module) for module in list(_import_submodules(sys.modules[module_name]))
)
return language_pack, supported_languages

View File

@ -1,20 +1,23 @@
import logging import logging
import os import os
import random
from enum import Enum from enum import Enum
from types import ModuleType from types import ModuleType
import language
import typer import typer
from rich.logging import RichHandler from rich.logging import RichHandler
from rich.console import Console from rich.console import Console
from rich.markdown import Markdown from rich.markdown import Markdown
from language import supported_languages
app = typer.Typer() app = typer.Typer()
app_state = {} app_state = {}
default_language = os.environ.get("FANLANG_DEFAULT_LANGUAGE", "common")
language_pack, supported_languages = language.load_language_pack()
Supported = Enum("Supported", ((k, k) for k in supported_languages.keys())) Supported = Enum("Supported", ((k, k) for k in supported_languages.keys()))
@ -34,21 +37,47 @@ def print_sample(lang: str, module: ModuleType) -> None:
@app.callback() @app.callback()
def main( def main(
language: Supported = typer.Option("common", help="The language to use."), language: Supported = typer.Option(
default=default_language,
help="The language to use."
),
): ):
app_state["language"] = supported_languages[language.name] app_state["language"] = supported_languages[language.name]
debug = os.getenv("DEBUG", None) debug = os.getenv("FANLANG_DEBUG", None)
logging.basicConfig( logging.basicConfig(
format="%(message)s", format="%(name)s %(message)s",
level=logging.DEBUG if debug else logging.INFO, level=logging.DEBUG if debug else logging.INFO,
handlers=[RichHandler(rich_tracebacks=True, tracebacks_suppress=[typer])], handlers=[RichHandler(rich_tracebacks=True, tracebacks_suppress=[typer])],
) )
logging.getLogger('markdown_it').setLevel(logging.ERROR)
logging.debug(f"Loaded language pack {language_pack}.")
logging.debug(f"Default language: {default_language}.")
@app.command() @app.command()
def words(count: int = typer.Option(50, help="The number of words to generate.")): def text(count: int = typer.Option(50, help="The number of words to generate.")):
print(" ".join(list(app_state["language"].Language.word(count))))
phrases = []
phrase = []
for word in app_state["language"].Language.word(count):
phrase.append(str(word))
if len(phrase) >= random.randint(1, 12):
phrases.append(' '.join(phrase))
phrase = []
if phrase:
phrases.append(' '.join(phrase))
paragraph = phrases[0].capitalize()
for phrase in phrases[1:]:
if random.choice([0, 0, 1]):
paragraph = paragraph + random.choice('?!.') + ' ' + phrase.capitalize()
else:
paragraph = paragraph + ', ' + phrase
paragraph = paragraph + random.choice('?!.')
console = Console(width=80)
console.print(paragraph)
@app.command() @app.command()

View File

@ -1,14 +0,0 @@
import importlib
import pkgutil
import sys
def import_submodules(module):
pkgs = pkgutil.iter_modules(module.__path__)
for loader, module_name, is_pkg in pkgs:
yield importlib.import_module(f"{module.__name__}.{module_name}")
supported_languages = dict(
(module.__name__.split(".")[-1], module) for module in list(import_submodules(sys.modules[__name__]))
)