Skip to content

kiesraad/pyeml_bindings

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python bindings for the EML_NL Standard

Note

This Python package makes it easier to work with EML_NL files in Python, but is no substitute for the official standard. Please use the official XSD files for validating EML_NL files.

Data bindings for the EML_NL Standard to load EML_NL files into Python dataclasses with correct structure, datatypes and typehints following the EML_NL Xml Schema Definitions. Type hints can be statically checked using mypy or pyright.

Requirements

  • At least Python version 3.10 for the KW_ONLY type annotations for dataclasses. This is so that non-nullable fields can be marked as mandatory (refer to xsdata documentation).
  • xsData for parsing using these data bindings.

For running tests, additionally:

Testing

The bindings are tested on over 2500 different EML files from different type of Dutch elections, all downloaded from data.overheid.nl using a roundtrip serialization test. To run the tests, install the test dependencies (see above), put EML files into a data folder and run pytest in the root folder.

Automated testing

Additionally, the bindings are tested against the election data specified in testfiles.txt. Due to the test duration, only the most recent election files are included in this test.

Usage

Make sure that xsData is installed. Using example file Telling_PS2023_Flevoland_gemeente_Almere.eml.xml which has EML id 510b:

from pathlib import Path
from xsdata.formats.dataclass.parsers import XmlParser
from xsdata.formats.dataclass.serializers import XmlSerializer
from xsdata.formats.dataclass.serializers.config import SerializerConfig
from pyeml_bindings import Eml510
from pyeml_bindings.namespace import NAMESPACE


# Create a parser object, can optionally be given extra config, see xsData docs
parser = XmlParser()

# Parse the eml file, specifying the dataclass to parse to.
eml = parser.from_path(Path("Telling_PS2023_Flevoland_gemeente_Almere.eml.xml"), Eml510)

# We can now access the data using standard Python syntax
# prints "Provinciale Staten Flevoland 2023"
print(eml.count.election.election_identifier.election_name)

# Or change some of the fields
eml.count.election.contests.contest[0].total_votes.cast = 1234

# And write back to EML using a serializer
serializer = XmlSerializer(config=SerializerConfig(xml_declaration=False))

with open(Path("output.xml"), "w") as out_file:
    # We can optionally pass the NAMESPACE from the bindings to the write function
    # to use the same namespace prefixes.
    # If we don't, we still get back valid EML but with ns0, ns1 etc.
    serializer.write(out=out_file, obj=eml, ns_map=NAMESPACE)

Building

To build the package yourself instead of installing the .whl, simply clone the repository and then in the root folder run.

python -m build

or you can simply install the package by running

python -m pip install .

Codegen

These bindings are mostly generated using xsData with some minor changes where needed. See commit history for these changes.