Parse Paradox Jomini data files to Python/JSON and revert them back experimentally.
Project description
ckparser
ckparser is a small Python utility for modding Paradox games that use the Jomini data format, such as Crusader Kings III or Europa Universalis V.
Its purpose is to convert Jomini data as faithfully as possible into Python data structures (dict, list, and native scalar types), and, when possible, convert Python/JSON data back into Jomini text.
The project can be used in two main ways:
- as a command-line tool to parse files or directories;
- as a Python library inside personal modding scripts.
It is primarily intended for modders who want to inspect, transform, export, or automate work on Paradox script data.
Features
Jomini → Python / JSON
- parse raw Jomini text into Python data structures;
- parse a single Jomini file into a dictionary;
- parse all
.txtfiles in a directory recursively; - optionally preserve comments;
- optionally detect file encodings automatically with
chardet; - partially resolve variables and inline formulas;
- collect global variables from
script_valuesbefore parsing the rest of a mod directory.
Python / JSON → Jomini
- convert Python dictionaries back into Jomini text;
- revert a JSON file into a Jomini-style text file;
- apply heuristic rules to decide whether a structure should be rendered as a list, a block, or repeated key/value pairs.
Important
Reverse conversion is currently experimental. It can work well on many simple or moderately structured cases, but it cannot guarantee a perfect reconstruction of all Jomini files.
Utilities
- convert
HSV/HLScolors toRGB; - convert Jomini dates (
YYYY.MM.DD) to Pythondatetime.date; - read files safely with encoding detection;
- walk deeply nested structures;
- parse Paradox localization files (
.yml).
Installation
From PyPI
pip install ckparser
Optional dependency
ckparser does not require external dependencies to run, but it can use chardet for automatic encoding detection.
pip install chardet
Without chardet, the parser still works, but encoding detection is disabled.
Command-line usage
The module can be executed directly with:
python -m ckparser <path>
Help
usage: ckparser.py [-h] [--encoding ENCODING] [--output OUTPUT] [--revert] [--comments] [--debug] path
Parse data from Paradox files in JSON or revert JSON files to Paradox format
positional arguments:
path path to a file or a directory to parse/revert
options:
-h, --help show this help message and exit
--encoding ENCODING encoding for reading/writing files
--output OUTPUT output directory for parsing results
--revert revert JSON files?
--comments include comments?
--debug debug mode?
Examples
Parse a single Jomini file:
python -m ckparser common/culture/cultures/my_cultures.txt
Parse a directory recursively and write JSON output:
python -m ckparser my_mod/common --output output
Include comments and/or dates in the parsed output:
python -m ckparser my_mod/common --comments --dates
Revert a JSON file back to Jomini format:
python -m ckparser data.json --revert
Enable debug logging:
python -m ckparser my_mod/common --debug
Output behavior
- in parsing mode, each
.txtfile can be converted to.json; - if parsing fails, the intermediate processed text can be saved as a
.errorfile for debugging; - a
ckparser.logfile is generated; - collected global variables can be saved to
_variables.json.
Library usage
Import
import ckparser
parse_text
Convert raw Jomini text into a Python structure.
from ckparser import parse_text
text = """
my_trigger = {
has_trait = brave
age >= 16
}
"""
data = parse_text(text)
print(data)
parse_file
Convert a Jomini file into a Python dictionary.
from ckparser import parse_file
data = parse_file("common/scripted_triggers/my_triggers.txt")
parse_all_files
Parse all .txt files in a directory recursively.
from ckparser import parse_all_files
result = parse_all_files("my_mod/common", keep_data=True)
Exmple with JSON export:
result = parse_all_files(
"my_mod/common",
output_dir="output",
save=True,
keep_data=False,
)
revert
Convert a Python structure back into Jomini text.
from ckparser import revert
data = {
"my_effect": {
"add_prestige": 100
}
}
text = revert(data)
print(text)
revert_file
Convert a JSON file into a Jomini text file.
from ckparser import revert_file
text = revert_file("my_data.json", save=True, output_dir="output")
convert_color
Convert supported color notations into an RGB hex string.
from ckparser import convert_color
print(convert_color(["hsv", 0.5, 0.8, 0.9]))
print(convert_color(["hsv360", 180, 80, 90]))
print(convert_color(["rgb", 255, 128, 0]))
convert_date
Convert a Jomini date into datetime.date.
from ckparser import convert_date
date = convert_date("1066.9.15")
print(date)
read_file
Read a file using the most appropriate encoding.
from ckparser import read_file
content = read_file("common/landed_titles/00_landed_titles.txt")
walk
Traverse a complex nested structure and yield terminal values with their logical path.
from ckparser import walk
for value, path in walk(data):
print(path, value)
parse_all_locales
Parse Paradox localization files.
from ckparser import parse_all_locales
locales = parse_all_locales("localization", language="english")
print(locales.get("my_key"))
Data model and design choices
Jomini is flexible, ambiguous, and not always internally consistent from the perspective of conventional programming data structures. ckparser therefore makes a number of practical decisions to produce useful Python output.
1. A {} block may represent either a dictionary or a list
In Jomini, the same block syntax can represent:
- a dictionary-like structure;
- a list of plain values;
- a list of nested blocks;
- or, in some cases, a mixed structure.
The parser attempts to infer the most reasonable Python representation from context.
2. Duplicate keys are converted into lists
In Jomini, the same key may appear multiple times inside the same block:
modifier = { factor = 2 }
modifier = { factor = 3 }
Since Python dictionaries cannot store duplicate keys, ckparser converts the value into a list:
{
"modifier": [
{"factor": 2},
{"factor": 3}
]
}
This is especially useful for structures involving repeated logical operators such as if, else_if, or, and, and similar constructs.
3. Non-standard operators are stored explicitly
Jomini supports operators such as:
=(for affectation and comparison)!=><>=<=?=(exists = ...)- ... and others
When an operator other than = is encountered, ckparser stores it explicitly:
{
"age": {
"@operator": ">=",
"@value": 16
}
}
This makes the original condition easier to preserve and inspect.
4. Variables and formulas
Jomini supports variable references and inline formulas, for example with @var or @[ ... ].
ckparser attempts to preserve:
- the original expression;
- the semantic type (
variableorformula); - the evaluated result, when available.
Example:
{
"some_value": {
"@type": "variable",
"@value": "@my_var",
"@result": 42
}
}
Or:
{
"scaled_value": {
"@type": "formula",
"@value": "@[base_value * 2]",
"@result": 84
}
}
5. Global variables and parsing order
Variable resolution often depends on:
- mod structure;
- file loading order;
- definitions stored in
script_values; - previously collected values.
For that reason, parse_all_files() can first parse files from script_values and register them as global variables before parsing the rest of the directory.
Comments
By default, comments are removed during parsing.
With comments=True or --comments, the parser attempts to preserve them in an internal technical representation so they remain available for debugging or later processing.
Comment preservation should be considered practical rather than perfectly lossless.
Dates
By default, dates are not converted in Python datetime.date objects in parsing.
While convenient, Jomini date format (Y[YYY].M[M].D[D]) does not enforce validation and can be invalid.
With dates=True or --dates, the parser attempts to convert these dates if possible and keeps invalid dates as text.
Localization parsing
ckparser also includes a dedicated parser for Paradox localization files:
from ckparser import parse_all_locales
locales = parse_all_locales("localization", language="english")
This supports:
- parsing a single
.ymlfile or an entire directory; - selecting a target language;
- building a
{key: value}dictionary.
The result can also be saved as JSON.
Example
Jomini input
my_entry = {
name = "Example"
age >= 16
is_active = yes
values = { 1 2 3 }
}
Python output
{
"my_entry": {
"name": "Example",
"age": {
"@operator": ">=",
"@value": 16
},
"is_active": True,
"values": [1, 2, 3]
}
}
JSON export
import json
from ckparser import parse_file
data = parse_file("example.txt")
print(json.dumps(data, indent=4, ensure_ascii=False))
Known limitations
Jomini is inherently ambiguous
Some Jomini blocks mix list-like and dictionary-like behavior in ways that cannot be represented perfectly in Python without making assumptions.
Reverse conversion is heuristic
The reverse transformation back to Jomini is still heuristic. It works on many straightforward cases, but it may not:
- reconstruct the exact original syntax;
- know whether a structure should be rendered as a list or as repeated keys;
- match the exact conventions expected by a specific game subsystem without additional rules.
Variable resolution depends on context
Formula and variable evaluation depends on:
- which files have already been parsed;
- what variables are known;
- the order in which parsing occurs;
- the mod’s internal structure.
As a result, some @result values may be missing or incomplete if the necessary context is not yet available.
Edge cases in Paradox scripting
Paradox scripting languages include many practical exceptions, formatting variants, and subsystem-specific conventions. ckparser aims to cover the most useful general cases, but not every possible edge case.
Typical use cases
- analyzing a mod programmatically;
- exporting Paradox files to JSON for inspection or diffing;
- writing migration or validation scripts;
- extracting data from landed_titles, script_values, events, and similar files;
- building personal Python tooling for Paradox modding workflows.
API summary
Parsing
parse_text(text, return_text_on_error=False, comments=False, dates=False, filename=None, is_global=False)parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=False, dates=False, is_global=False, patch=None)parse_all_files(path, output_dir=None, encoding="utf_8_sig", keep_data=False, save=False, comments=False, dates=False, variables_first=True)
Reversion
revert(obj, from_key=None, prev_key=None, depth=-1, sep="\t", sort=False)revert_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False)
Utilities
convert_color(color)convert_date(date, key=None)read_file(path, encoding="utf_8_sig")walk(obj, *from_keys)parse_all_locales(path, encoding="utf_8_sig", language="english", save=False)load_variables(filepath="_variables.json")save_variables(filepath="_variables.json")
Project status
ckparser is intended as a practical modding tool rather than a formal reference implementation of the Jomini format. The parser is already useful for real automation tasks, while some parts, especially reverse conversion, remain intentionally pragmatic and experimental.
Real-world examples, edge cases, and mod-specific rules are valuable for improving coverage over time.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ckparser-0.3.2.tar.gz.
File metadata
- Download URL: ckparser-0.3.2.tar.gz
- Upload date:
- Size: 16.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6586479749fbc91156ffce5802e22137f2c5411cc11b6fdfa774c71862e56ad9
|
|
| MD5 |
321ee631a73df588e6775e12ccd5e510
|
|
| BLAKE2b-256 |
c1bc2039de85a7b53653a8370a191667d84fb31a144f1f4bd786eb42281a3154
|
File details
Details for the file ckparser-0.3.2-py3-none-any.whl.
File metadata
- Download URL: ckparser-0.3.2-py3-none-any.whl
- Upload date:
- Size: 16.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aae2f1a8df4c896badf95d8c9fde3b03123f4535076f21d91901f09219b63c89
|
|
| MD5 |
006d391a8715af52ddcad42eade003a3
|
|
| BLAKE2b-256 |
44ee6d0dca72b45ac469660f911957a8f2d553c90f638e6a1edbd344dda93a1d
|