What is Pydifact?
masterEDIFACT files. It is intended for handling the EDIFACT standard, which is used in various business sectors (such as medical report transfers in Austria) for data exchange.repository·master·Indexed 41 days ago
https://github.com/nerdocs/pydifactA Python library for reading and writing EDIFACT files. It provides a Parser for converting raw EDIFACT strings into structured data, a Serializer for converting Segment objects back to strings, and a hierarchy of containers including Interchange and Message to manage EDIFACT documents. The library includes a Plugin API via PluginMount for extensibility and a generator for managing directory/source files from providers like UNECE and GEFEG.
EDIFACT files. It is intended for handling the EDIFACT standard, which is used in various business sectors (such as medical report transfers in Austria) for data exchange.Pydifact provides a framework for extending its functionality via plugins, following the Simple Plugin Framework pattern. To create a plugin, you can use the PluginMount metaclass.
Specifically, the SegmentProvider class in pydifact.segments uses PluginMount, meaning it is designed to be extensible through plugins.
The pydifact-generator requires specific source files to function. These files must be placed in the Zips directory, which is the location where the generator searches for its data sources.
Because these sources are hosted externally, they are not bundled with the pydifact package. You must manually download the necessary ZIP files from the following providers:
Note: The generator validates the presence of these files. If a required ZIP file for a specific release is missing from the Zips directory, the generator will throw an error.
The Segment class is a low-level internal class. To implement specialized EDIFACT segments, you should subclass Segment.
When subclassing, you must:
tag attribute (the 3-letter uppercase alphanumeric segment code).validate() method to handle segment-specific validation logic.If you do not set __omitted__ = True in your subclass, it will be automatically registered in the Segment.plugins list, making it available via the SegmentFactory.
from pydifact.segments import Segment
class MyCustomSegment(Segment):
tag = "ABC"
def validate(self, syntax_version: str, directory: str) -> None:
# Custom validation logic here
passPydifact uses a hierarchy of segment containers to represent EDIFACT data structures. All containers inherit from AbstractSegmentsContainer, which provides common methods for managing a collection of Segment objects.
There are three main types of containers:
Interchange: Represents the highest level of an EDIFACT document. It includes a header (UNB) and a footer (UNZ) and can contain one or more Message objects.Message: Represents a single message within an interchange, bounded by a header (UNH) and a footer (UNT).RawSegmentCollection: A lightweight container for analyzing arbitrary groups of segments without the overhead of header/footer validation or specific EDIFACT structure requirements.When using Interchange or Message, the container automatically manages the envelope segments (header and footer) separately from the internal segments list.
from pydifact.segmentcollection import Interchange
# An interchange can be created from a string
interchange = Interchange.from_str("UNB+UNOA:1+SENDER+RECIPIENT+...\n...UNT+1+REF\nUNZ+1+REF")
# Or from a file
interchange = Interchange.from_file("path/to/edi_file.txt")The Interchange class provides factory methods to instantiate an EDIFACT interchange from raw data.
Interchange.from_str(string, parser=None, characters=None): Parses an EDIFACT string into an Interchange instance.Interchange.from_file(file, encoding='iso8859-1', parser=None): Reads an EDIFACT file from disk using the specified encoding and parses it.Note: If the input contains a UNA segment, the Interchange will automatically update its control characters and set the has_una_segment flag.
from pydifact.segmentcollection import Interchange
# From string
interchange = Interchange.from_str("UNB+...\n...UNZ+...")
# From file
interchange = Interchange.from_file("data.edi", encoding="utf-8")An Interchange can contain multiple messages. Use get_messages() to iterate through them. This method parses the segments between UNH (header) and UNT (footer) tags into Message objects.
If the interchange structure is invalid (e.g., a UNH is not closed by a UNT), an EDISyntaxError will be raised.
for message in interchange.get_messages():
print(f"Message Type: {message.type}, Version: {message.version}")
# Process message segments...The Parser.get_control_characters static method allows you to extract the EDI control characters from a message string (specifically looking for the UNA segment) without performing a full parse.
This is useful if you need to know the delimiters used in a file before deciding how to process it. If the message does not start with UNA, it returns the provided characters object (which defaults to standard EDIFACT characters if none are provided).
from pydifact.parser import Parser
from pydifact.control import Characters
# Example message starting with UNA
message = "UNA:+.? '"
# Extract characters
chars = Parser.get_control_characters(message)
print(chars.segment_terminator) # Output depends on the UNA contentYou can customize the Parser behavior during initialization using the following parameters:
factory: A SegmentFactory instance used to create segment objects. Defaults to a new SegmentFactory().characters: A Characters instance defining the control characters (separators, terminators, etc.). Defaults to a new Characters() instance.directory: A string representing the directory used for looking up segment definitions. Defaults to an empty string.Note: If you provide characters during the parse() call, those will take precedence over the parser's initialized characters if no UNA segment is found in the message.
from pydifact.parser import Parser
from pydifact.segments import SegmentFactory
from pydifact.control import Characters
custom_factory = SegmentFactory()
custom_chars = Characters()
parser = Parser(
factory=custom_factory,
characters=custom_chars,
directory="/path/to/segments"
)To convert an Interchange or Message back into a raw EDIFACT string, use the serialize() method. This method automatically includes the header and footer segments.
serialize(break_lines=False): Returns the string representation. If break_lines is True, a line break is inserted after each segment terminator (note: this is common in practice but technically forbidden in some EDIFACT specs).__str__(): Calling str(container) is equivalent to calling serialize().edi_string = interchange.serialize(break_lines=True)The core data structures in pydifact are:
Segment: Represents a single EDIFACT segment (a group of elements).Token: Represents the smallest unit of data within a segment.Tokenizer: Used for breaking down raw text into Token objects.Characters: Handles EDIFACT-specific character sets and control characters.You can manually trigger validation for a Segment instance using the validate() method. This checks the segment's elements against an XML schema definition located in the pydifact syntax directories.
Parameters:
syntax_version (str): The EDIFACT syntax version (e.g., "1", "300", "402"). The correct release is determined automatically.directory (str): The directory name to validate against (e.g., "d00a", "d96a").Validation Logic:
an for alphanumeric, n for numeric, a for alphabetic) and length constraints (length or maxlength).Raises:
ValidationError: If the segment does not conform to the schema.# Assuming 'segment' is an instance of a Segment subclass
try:
segment.validate(syntax_version="402", directory="d96a")
except ValidationError as e:
print(f"Validation failed: {e}")