srctools.keyvalues

Reads and parses Valve’s KeyValues files.

These files follow the following general format:

"Name"
    {
    "Name" "Value" // Comment
    "Name"
        {
        "Name" "Value"
        }
    "Name" "Value"
    "SecondName" "ps3-only value" [ps3]
    "Third_Name" "never on Linux" [!linux]
    "Name" "multi-line values
are supported like this.
They end with a quote."
    }

The names are usually case-insensitive. Call Keyvalues(name, value) to get a keyvalues object, or Keyvalues.parse(file, 'filename') to parse a file.

This will perform a round-trip file read:

>>> with open('filename.txt', 'r') as read_file:  
...     kv = Keyvalues.parse(read_file, 'filename.txt')
... with open('filename_2.txt', 'w') as write_file:
...     kv.serialise(write_file)

Keyvalue values should be either a string, or a list of children Properties. Names will be converted to lowercase automatically; use Prop.real_name to obtain the original spelling. To allow multiple root blocks in a file, the returned keyvalues from Keyvalues.parse() is special and will export with un-indented children.

Properties with children can be indexed by their names, or by a (‘name’, default) tuple:

>>> kv = Keyvalues('Top', [
...     Keyvalues('child1', '1'),
...     Keyvalues('child2', '0'),
... ])
>>> kv['child1']
'1'
>>> kv['child3']
Traceback (most recent call last):
    ...
IndexError: No key child3!
>>> kv['child3', 'default']
'default'
>>> kv['child4', object()]
<object object at 0x...>
>>> del kv['child2']
>>> kv['child3'] = 'new value'
>>> kv
Keyvalues('Top', [Keyvalues('child1', '1'), Keyvalues('child3', 'new value')])

Handling \n, \t, \", and \\ escape characters can be enabled.

exception srctools.keyvalues.KeyValError(
message: str,
file: str | _os.PathLike[str] | None = None,
line: int | None = None,
)

An error that occurred when parsing a Valve KeyValues file.

See the base class TokenSyntaxError for available attributes.

exception srctools.keyvalues.NoKeyError(key: str)

Raised if a key is not found when searching with find_key(), find_block(), etc.

key: str

The key that was missing.

exception srctools.keyvalues.LeafKeyvalueError(
leaf: Keyvalues,
operation: str,
)

Raised when a method requiring a keyvalue block is run on a leaf keyvalue.

Leaf keyvalues only have a string value, so this operation is not valid.

leaf: Keyvalues

The keyvalue being used.

operation: str

Name of the operation being performed.

class srctools.keyvalues.Keyvalues(
name: str | None,
value: List[Keyvalues] | str,
)

Represents Valve’s Keyvalues 1 file format.

Value should be a string (for leaf properties), or a list of children Keyvalues objects. The name should be a string, or None for a root object. Root objects export each child at the topmost indent level. This is produced from Keyvalues.parse() calls.

property value: str

Return the value of a leaf keyvalue.

Deprecated:

Accessing the internal list for keyvalue blocks.

property name: str

Produces a str.casefold()-ed version of the keyvalue’s name. Usually names are case-insensitive.

Assigning to this automatically updates both this and real_name.

Deprecated:

‘Root’ keyvalues have a name of None. This will be replaced by a blank string, use is_root() instead.

property real_name: str

The original, case-sensitive version of this name.

Assigning to this automatically updates both this and name.

Deprecated:

‘Root’ keyvalues have a name of None. This will be replaced by a blank string, use is_root() instead.

edit(
name: str | None = None,
value: str | None = None,
) Keyvalues

Simultaneously modify the name and value.

classmethod root(
*children: Keyvalues,
) Keyvalues

Return a new ‘root’ keyvalue. These have no name, and are returned from parse().

When serialised, their children are directly written with no indents, allowing multiple keyvalues blocks to exist on the topmost level.

static parse(
file_contents: str | BaseTokenizer | Iterator[str],
filename: str | _os.PathLike[str] = '',
*,
flags: Mapping[str, bool] = srctools.EmptyMapping,
newline_keys: bool = False,
newline_values: bool = True,
allow_escapes: bool = True,
single_line: bool = False,
) Keyvalues

Returns a Keyvalues tree parsed from given text.

Parameters:
  • file_contents – should be an iterable of strings or a single string. Alternatively, file_contents may be an already created tokenizer. In this case allow_escapes is ignored.

  • filename – If set this should be the source of the text for debug purposes. If not supplied, file_contents.name will be used if present.

  • flags – This should be a mapping for additional [flag] suffixes to accept.

  • allow_escapes – This allows choosing if \t or similar escapes are parsed.

  • single_line – If this is set, allow multiple properties to be on the same line. This means unterminated strings will be caught late (if at all), but it allows parsing some ‘internal’ data blocks.

  • newline_keys – This specifies if newline characters are allowed in keys. Keys are prohibited by default, since this is fairly useless, but if quote characters are mismatched it’ll catch the mistake early.

  • newline_values – This specifies if newline characters are allowed in string values.

find_all(
*keys: str,
) Iterator[Keyvalues]

Search through the tree, yielding all properties that match a particular path.

find_children(
*keys: str,
) Iterator[Keyvalues]

Search through the tree, yielding children of properties in a path.

find_key(
key: str,
def_: str = <object object>,
*,
or_blank: bool = False,
) Keyvalues

Obtain the child Keyvalue with a given name.

  • If no child is found with the given name, this will return the default value wrapped in a new Keyvalue. If or_blank is set, it will be a blank block instead. If neither default is provided this will raise NoKeyError.

  • This prefers keys located closer to the end of the value list.

find_block(
key: str,
or_blank: bool = False,
) Keyvalues

Obtain the child Keyvalue block with a given name.

  • If no child is found with the given name and or_blank is true, a blank Keyvalues block will be returned. Otherwise, NoKeyError will be raised.

  • This prefers keys located closer to the end of the value list.

int(
key: str,
def_: int | T = 0,
) int | T

Return the value of an integer key.

Equivalent to int(prop[key]), but with a default value if missing or invalid. If multiple keys with the same name are present, this will use the last only.

float(
key: str,
def_: float | T = 0.0,
) float | T

Return the value of an integer key.

Equivalent to float(prop[key]), but with a default value if missing or invalid. If multiple keys with the same name are present, this will use the last only.

bool(
key: str,
def_: bool | T = False,
) bool | T

Return the value of a boolean key.

The value may be case-insensitively ‘true’, ‘false’, ‘1’, ‘0’, ‘T’, ‘F’, ‘y’, ‘n’, ‘yes’, or ‘no’. If multiple keys with the same name are present, this will use the last only.

vec(
key: str,
x: float = 0.0,
y: float = 0.0,
z: float = 0.0,
) Vec

Return the given keyvalue, converted to a vector.

If multiple keys with the same name are present, this will use the last only.

set_key(
path: Tuple[str, ...] | str,
value: str,
) None

Set the value of a key deep in the tree hierarchy.

  • If any of the hierarchy do not exist (or do not have children), blank properties will be added automatically.

  • path should be a tuple of names, or a single string.

copy() Keyvalues

Deep copy this Keyvalue tree and return it.

as_dict() str | Dict[str, str | Dict[str, _As_Dict_Ret]]

Convert this keyvalue tree into a tree of dictionaries.

This keeps only the last if multiple items have the same name.

as_array(*, conv: ~typing.Callable[[str], ~srctools.keyvalues.T] = <class 'str'>) List[T] | List[str]

Convert a keyvalue block into a list of values.

If the keyvalue is a single keyvalue, the single value will be yielded. Otherwise, each child must be a single value and each of those will be yielded. The name is ignored.

iter_tree(
blocks: bool = False,
) Iterator[Keyvalues]

Iterate through all keyvalues in this tree.

This goes through keyvalues in the same order that they will serialise into. If blocks is True, the keyvalue blocks will be returned as well as keyvalues. If false, only keyvalues will be yielded.

clear() None

Delete the contents of a block.

append(
other: Iterable[Keyvalues] | Keyvalues,
) None

Append another keyvalue to this one.

Deprecated behaviour: Accept an iterable of properties or a root keyvalue which are merged into this one.

extend(
other: Iterable[Keyvalues],
) None

Extend this keyvalue with the contents of another, or an iterable.

merge_children(*names: str) None

Merge together any children of ours with the given names.

After execution, this tree will have only one sub-Keyvalue for each of the given names. Direct leaf keyvalues will be left unchanged, even if they happen to match the given names.

ensure_exists(
key: str,
) Keyvalues

Ensure a Keyvalue block exists with this name, and return it.

has_children() bool

Does this have child properties?

is_root() bool

Check if the keyvalue is a root, returned from the parse() or root() methods.

See root() for more information.

serialise(
file: _SupportsWrite | None = None,
/,
*,
indent: str = '\t',
indent_braces: bool = True,
) str | None

Serialise the keyvalues data to a file, or return as a string.

Recursive trees are not permitted.

Parameters:
  • file – The file to write to. If omitted, the data is returned instead.

  • indent – The characters to use for each indentation.

  • indent_braces – If enabled, indent the braces to match the block contents, instead of the name.

serialize(
file: _SupportsWrite | None = None,
/,
*,
indent: str = '\t',
indent_braces: bool = True,
) str | None

Serialise the keyvalues data to a file, or return as a string.

Recursive trees are not permitted.

Parameters:
  • file – The file to write to. If omitted, the data is returned instead.

  • indent – The characters to use for each indentation.

  • indent_braces – If enabled, indent the braces to match the block contents, instead of the name.

export() Iterator[str]

Generate the set of strings for a keyvalues file.

Recursively calls itself for all child properties.

Deprecated:

Use serialise() instead.

build() _Builder

Allows appending a tree to this keyvalue in a convenient way.

Use as follows:

# doctest: +NORMALIZE_WHITESPACE
>>> kv = Keyvalues('name', [])
>>> with kv.build() as builder:
...     builder.root1('blah')
...     builder.root2('blah')
...     with builder.subprop:
...         subprop = builder.config('value')
...         builder['unusual name']('value')
Keyvalues('root1', 'blah')
Keyvalues('root2', 'blah')
Keyvalues('unusual name', 'value')
>>> print(subprop) # doctest: +NORMALIZE_WHITESPACE
"config" "value"
>>> print(''.join(kv.export())) # doctest: +NORMALIZE_WHITESPACE
"name"
    {
    "root1" "blah"
    "root2" "blah"
    "subprop"
        {
        "config" "value"
        "unusual name" "value"
        }
    }

The return values/results of the context manager are the properties. Set names by builder.name, builder['name']. For keywords append _.

Alternatively:

>>> with Keyvalues('name', []).build() as builder:
...     builder.root1('blah')
...     builder.root2('blah')
Keyvalues('root1', 'blah')
Keyvalues('root2', 'blah')
>>> kv = builder()
>>> print(repr(kv))
Keyvalues('name', [Keyvalues('root1', 'blah'), Keyvalues('root2', 'blah')])
srctools.keyvalues.escape_text(text: str) str

Escape special characters and backslashes, so tokenising reproduces them.

Specifically: \\, ", tab, and newline.