clubsandwich.datastore
¶
Convenient access to spreadsheet-style game data files. CSV is the only included implementation, but writing new readers is trivial.
Here’s a simple example:
def f_char(val):
"Ensure *val* is a one-character string"
if isinstance(val, str) and len(val) == 1:
return val
else:
raise ValueError("Bad character value: {!r}".format(val))
# Imagine you have a directory of CSVs with monster definitions.
# One of them, gnomes.csv, might look like this:
# ID, Strength, Color, Char
# gnome_easy, 1, #ff0000, g
# gnome_medium, 2, #ffff00, g
# gnome_hard, 3, #ff00ff, g
# gnome_huge, 4, #880000, G
# To read them, create a :py:class:`DataStore`, passing a class name
# for items and an ordered mapping of field name to value:
monster_types = DataStore('MonsterType', (
('id', str),
('strength', int), # int('5') = 5
('color', str),
('char', f_char), # see helper at top of example
))
# You can load all the CSVs at once, like this:
monster_types.add_sources_with_glob('data/monsters/*.csv', CSVReader)
# Now you can access any monster type easily:
print(monster_types.gnome_easy)
# > MonsterType(id='gnome_easy', strength=1, color='#ff0000', char='g')
print(monster_types['gnome_easy'])
# > same thing
# And if you edit your data files, you can reload them at runtime to
# immediately pick up changes:
monster_types.reload()
Limitations and details:
- The first field in the mapping must be a unique identifier, unique across all readers.
- Identifiers may overlap built-in Python method names or start with numbers.
To access these values by attribute, prefix them with
r_
. So a value with id5
could be accessed bydata_store.r_5
. Or you could just use dict-style lookup, likedata_store['5']
. To avoid these issues, use Python-identifier-legal uppercase string identifiers. - You may pass default values to the
DataStore
. These only matter if your data may be missing keys (i.e. if your reader emitsdict``s), so you can just pass ``None
if you want.
-
class
clubsandwich.datastore.
CSVReader
(path, skip_first_line=True)¶ Returns the lines from a single CSV file. If your first line is just column labels, you can skip it (this is the default behavior).
-
path
¶ Path to the file
-
identifier
¶
-
skip_first_line
¶ If
True
(default), always skip the first line of the file.
-
read
()¶ Iterator of CSV values. Values are lists.
-
-
class
clubsandwich.datastore.
DataStore
(type_name, fields, defaults=None)¶ Collection of data sources for convenient access. See example in module docs for a basic guide.
-
add_sources_with_glob
(pattern, reader_factory)¶ Parameters: - pattern (str) – Glob pattern (docs)
- reader_factory (function) –
reader_factory(path) -> Reader
Add multiple sources for paths that match the given pattern (glob). For example, if you wanted to read all CSV files in your “monsters” directory, you’d do this:
data_store.add_sources_with_glob('data/monsters/*.csv', CSVReader)
-
items
¶ Iterator of all items
-
keys
()¶ Iterator of all item identifiers
-
reload
()¶ Reload all sources. Does not pick up new files if you used a glob. To handle that case, call
unload()
and then re-add all your sources.
-
unload
()¶ Remove all sources.
-
-
class
clubsandwich.datastore.
Reader
¶ Abstract base class for a reader. You can subclass this for your own readers if you want, but duck typing is probably less verbose.
-
identifier
¶ String identifier for this reader. Only used to provide helpful exception messages.
-
read
()¶ Iterator of sequences or dicts from the file or data structure. Like
('a', 'b', 'c')
or{'f1': 'a', 'f2': 'b', 'f3': 'c'}
.
-
-
class
clubsandwich.datastore.
Source
(reader, row_class, fields, defaults=None)¶ Stores and indexes values from a reader. You shouldn’t need to worry about this class much; it’s created automatically for you, and sources are accessed in aggregate via a
DataStore
.-
row_class
¶ Class to instantiate for each row
-
fields
¶ Sequence of pairs mapping field name to value factory
-
defaults
¶ Default values for the fields, or
None
if you’re quite sure all values will be specified
-
items
¶ List of items read from the source
-
keys
()¶ Iterator of all keys in this source
-
reload
()¶ Run the reader again; replace all stored items
-