pyx12 4.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyx12/__init__.py +10 -0
- pyx12/codes.py +120 -0
- pyx12/dataele.py +103 -0
- pyx12/decorators.py +79 -0
- pyx12/errh_xml.py +324 -0
- pyx12/error_997.py +457 -0
- pyx12/error_999.py +391 -0
- pyx12/error_debug.py +148 -0
- pyx12/error_handler.py +1358 -0
- pyx12/error_html.py +255 -0
- pyx12/error_item.py +102 -0
- pyx12/error_visitor.py +80 -0
- pyx12/errors.py +73 -0
- pyx12/map/270.4010.X092.A1.xml +2995 -0
- pyx12/map/271.4010.X092.A1.xml +4874 -0
- pyx12/map/276.4010.X093.A1.xml +2166 -0
- pyx12/map/277.4010.X093.A1.xml +2669 -0
- pyx12/map/277.5010.X212.xml +4179 -0
- pyx12/map/277.5010.X214.xml +2605 -0
- pyx12/map/277U.4010.X070.xml +2668 -0
- pyx12/map/278.4010.X094.27.A1.xml +7321 -0
- pyx12/map/278.4010.X094.A1.xml +9385 -0
- pyx12/map/820.4010.X061.A1.xml +2313 -0
- pyx12/map/820.5010.X218.v2.xml +1029 -0
- pyx12/map/820.5010.X218.xml +3142 -0
- pyx12/map/830.4010.PS.xml +881 -0
- pyx12/map/834.4010.X095.A1.xml +4032 -0
- pyx12/map/834.5010.X220.A1.v2.xml +2145 -0
- pyx12/map/834.5010.X220.A1.xml +5127 -0
- pyx12/map/835.4010.X091.A1.xml +3587 -0
- pyx12/map/835.5010.X221.A1.v2.xml +1410 -0
- pyx12/map/835.5010.X221.A1.xml +4233 -0
- pyx12/map/837.4010.X096.A1.xml +39589 -0
- pyx12/map/837.4010.X097.A1.xml +11478 -0
- pyx12/map/837.4010.X098.A1.xml +23842 -0
- pyx12/map/837.5010.X222.A1.xml +25900 -0
- pyx12/map/837Q3.I.5010.X223.A1.v2.xml +9987 -0
- pyx12/map/837Q3.I.5010.X223.A1.xml +30108 -0
- pyx12/map/841.4010.XXXC.xml +865 -0
- pyx12/map/997.4010.xml +961 -0
- pyx12/map/999.5010.xml +1283 -0
- pyx12/map/999.5010X231.A1.xml +1283 -0
- pyx12/map/README +16 -0
- pyx12/map/README.md +20 -0
- pyx12/map/codes.xml +2844 -0
- pyx12/map/codes.xsd +40 -0
- pyx12/map/comp_test.xml +69 -0
- pyx12/map/dataele.xml +363 -0
- pyx12/map/dataele.xsd +60 -0
- pyx12/map/dump.py +22 -0
- pyx12/map/map.v2.xsd +135 -0
- pyx12/map/map.xsd +150 -0
- pyx12/map/maps.xml +85 -0
- pyx12/map/maps.xsd +109 -0
- pyx12/map/ord.py +39 -0
- pyx12/map/x12.control.00401.xml +386 -0
- pyx12/map/x12.control.00501.xml +369 -0
- pyx12/map/x12simple.dtd +16 -0
- pyx12/map_if/__init__.py +39 -0
- pyx12/map_if/_base.py +189 -0
- pyx12/map_if/_composite.py +160 -0
- pyx12/map_if/_element.py +391 -0
- pyx12/map_if/_loader.py +58 -0
- pyx12/map_if/_loop.py +318 -0
- pyx12/map_if/_root.py +222 -0
- pyx12/map_if/_segment.py +504 -0
- pyx12/map_index.py +146 -0
- pyx12/map_override.py +45 -0
- pyx12/map_walker.py +695 -0
- pyx12/nodeCounter.py +88 -0
- pyx12/params.py +160 -0
- pyx12/path.py +218 -0
- pyx12/rawx12file.py +98 -0
- pyx12/scripts/__init__.py +22 -0
- pyx12/scripts/x12html.py +166 -0
- pyx12/scripts/x12info.py +107 -0
- pyx12/scripts/x12norm.py +106 -0
- pyx12/scripts/x12valid.py +179 -0
- pyx12/scripts/x12xml.py +153 -0
- pyx12/scripts/xmlx12.py +105 -0
- pyx12/segment.py +589 -0
- pyx12/syntax.py +147 -0
- pyx12/validation.py +275 -0
- pyx12/version.py +1 -0
- pyx12/x12context.py +1191 -0
- pyx12/x12file.py +687 -0
- pyx12/x12metadata.py +278 -0
- pyx12/x12n_document.py +278 -0
- pyx12/x12xml.py +234 -0
- pyx12/x12xml_simple.py +128 -0
- pyx12/xmlwriter.py +155 -0
- pyx12/xmlx12_simple.py +60 -0
- pyx12-4.0.0rc1.dist-info/METADATA +173 -0
- pyx12-4.0.0rc1.dist-info/RECORD +98 -0
- pyx12-4.0.0rc1.dist-info/WHEEL +5 -0
- pyx12-4.0.0rc1.dist-info/entry_points.txt +7 -0
- pyx12-4.0.0rc1.dist-info/licenses/LICENSE.txt +28 -0
- pyx12-4.0.0rc1.dist-info/top_level.txt +1 -0
pyx12/__init__.py
ADDED
pyx12/codes.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
######################################################################
|
|
2
|
+
# Copyright
|
|
3
|
+
# John Holland <john@zoner.org>
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This software is licensed as described in the file LICENSE.txt, which
|
|
7
|
+
# you should have received as part of this distribution.
|
|
8
|
+
#
|
|
9
|
+
######################################################################
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
External Codes interface
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import os.path
|
|
19
|
+
from importlib.resources import files as _res_files
|
|
20
|
+
from typing import IO, Any, TypedDict
|
|
21
|
+
|
|
22
|
+
import defusedxml.ElementTree as et
|
|
23
|
+
|
|
24
|
+
from pyx12.errors import EngineError
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CodesError(Exception):
|
|
28
|
+
"""Class for code modules errors."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _CodeSet(TypedDict):
|
|
32
|
+
name: str | None
|
|
33
|
+
dataele: str | None
|
|
34
|
+
codes: frozenset[str | None]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ExternalCodes:
|
|
38
|
+
"""
|
|
39
|
+
Validates an ID against an external list of codes
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
codes: dict[str | None, _CodeSet]
|
|
43
|
+
exclude_list: list[str]
|
|
44
|
+
|
|
45
|
+
def __init__(self, base_path: str | None = None, exclude: str | None = None) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Initialize the external list of codes
|
|
48
|
+
|
|
49
|
+
:param base_path: Override directory containing codes.xml.
|
|
50
|
+
If None, uses package resource folder.
|
|
51
|
+
:param exclude: comma-separated string of external code names to ignore.
|
|
52
|
+
|
|
53
|
+
``self.codes`` maps codeset_id to ``{'name', 'dataele', 'codes'}``.
|
|
54
|
+
"""
|
|
55
|
+
logger = logging.getLogger("pyx12")
|
|
56
|
+
self.codes = {}
|
|
57
|
+
codes_file = "codes.xml"
|
|
58
|
+
# ElementTree.parse accepts either text- or binary-mode streams; the
|
|
59
|
+
# file source differs between override path and bundled package resource.
|
|
60
|
+
code_fd: IO[Any]
|
|
61
|
+
if base_path is not None:
|
|
62
|
+
logger.debug(f"Looking for codes file '{codes_file}' in map_path '{base_path}'")
|
|
63
|
+
code_fd = open(os.path.join(base_path, codes_file), encoding="utf-8")
|
|
64
|
+
else:
|
|
65
|
+
logger.debug(f"Looking for codes file '{codes_file}' in package resources")
|
|
66
|
+
code_fd = _res_files("pyx12").joinpath("map", codes_file).open("rb")
|
|
67
|
+
|
|
68
|
+
self.exclude_list = exclude.split(",") if exclude is not None else []
|
|
69
|
+
with code_fd:
|
|
70
|
+
parser = et.XMLParser(encoding="utf-8")
|
|
71
|
+
for cElem in et.parse(code_fd, parser=parser).iter("codeset"):
|
|
72
|
+
codeset_id = cElem.findtext("id")
|
|
73
|
+
self.codes[codeset_id] = {
|
|
74
|
+
"name": cElem.findtext("name"),
|
|
75
|
+
"dataele": cElem.findtext("data_ele"),
|
|
76
|
+
"codes": frozenset(c.text for c in cElem.iterfind("version/code")),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def isValid(self, key: str | None, code: str | None, check_dte: str | None = None) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Return True if code is in the codeset identified by key.
|
|
82
|
+
"""
|
|
83
|
+
if not key:
|
|
84
|
+
raise EngineError(f"bad key {key!r}")
|
|
85
|
+
if key in self.exclude_list:
|
|
86
|
+
return True
|
|
87
|
+
if key not in self.codes:
|
|
88
|
+
raise EngineError(f'External Code "{key}" is not defined')
|
|
89
|
+
return code in self.codes[key]["codes"]
|
|
90
|
+
|
|
91
|
+
def debug_print(self, count: int = 10) -> None:
|
|
92
|
+
"""Debug print first <count> codes for each codeset (alphabetical)."""
|
|
93
|
+
for key in self.codes:
|
|
94
|
+
print(sorted(self.codes[key]["codes"], key=lambda c: c or "")[:count])
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def list_external_codesets(base_path: str | None = None) -> list[tuple[str, str]]:
|
|
98
|
+
"""Return ``(id, name)`` for every codeset in ``codes.xml``.
|
|
99
|
+
|
|
100
|
+
Used by CLI scripts to populate ``--help`` with the list of names
|
|
101
|
+
accepted by the ``--exclude-external-codes`` flag. Loads the metadata
|
|
102
|
+
only — does not parse code values — so the cost is small.
|
|
103
|
+
|
|
104
|
+
:param base_path: Override directory containing codes.xml.
|
|
105
|
+
If None, uses package resource folder.
|
|
106
|
+
"""
|
|
107
|
+
code_fd: IO[Any]
|
|
108
|
+
if base_path is not None:
|
|
109
|
+
code_fd = open(os.path.join(base_path, "codes.xml"), encoding="utf-8")
|
|
110
|
+
else:
|
|
111
|
+
code_fd = _res_files("pyx12").joinpath("map", "codes.xml").open("rb")
|
|
112
|
+
pairs: list[tuple[str, str]] = []
|
|
113
|
+
with code_fd:
|
|
114
|
+
parser = et.XMLParser(encoding="utf-8")
|
|
115
|
+
for cs in et.parse(code_fd, parser=parser).iter("codeset"):
|
|
116
|
+
cid = (cs.findtext("id") or "").strip()
|
|
117
|
+
name = (cs.findtext("name") or "").strip()
|
|
118
|
+
if cid:
|
|
119
|
+
pairs.append((cid, name))
|
|
120
|
+
return pairs
|
pyx12/dataele.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
######################################################################
|
|
2
|
+
# Copyright (c) 2001-2019
|
|
3
|
+
# John Holland <john@zoner.org>
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This software is licensed as described in the file LICENSE.txt, which
|
|
7
|
+
# you should have received as part of this distribution.
|
|
8
|
+
#
|
|
9
|
+
######################################################################
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
Interface to normalized Data Elements
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import os.path
|
|
19
|
+
from importlib.resources import files as _res_files
|
|
20
|
+
from typing import IO, Any, TypedDict
|
|
21
|
+
|
|
22
|
+
import defusedxml.ElementTree as et
|
|
23
|
+
|
|
24
|
+
# Intrapackage imports
|
|
25
|
+
from pyx12.errors import EngineError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DataElementsError(Exception):
|
|
29
|
+
"""Class for data elements module errors."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _DataEle(TypedDict):
|
|
33
|
+
data_type: str | None
|
|
34
|
+
min_len: int
|
|
35
|
+
max_len: int
|
|
36
|
+
name: str | None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DataElements:
|
|
40
|
+
"""
|
|
41
|
+
Interface to normalized Data Elements
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
dataele: dict[str | None, _DataEle]
|
|
45
|
+
|
|
46
|
+
def __init__(self, base_path: str | None = None) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Initialize the list of data elements
|
|
49
|
+
|
|
50
|
+
:param base_path: Override directory containing dataele.xml. If None,
|
|
51
|
+
uses package resource folder
|
|
52
|
+
:type base_path: string
|
|
53
|
+
|
|
54
|
+
Note: ``self.dataele`` maps to the data element
|
|
55
|
+
``{ele_num: {data_type, min_len, max_len, name}}``
|
|
56
|
+
"""
|
|
57
|
+
logger = logging.getLogger("pyx12")
|
|
58
|
+
self.dataele = {}
|
|
59
|
+
dataele_file = "dataele.xml"
|
|
60
|
+
# ElementTree.parse accepts either text- or binary-mode streams; the
|
|
61
|
+
# file source differs between override path and bundled package resource.
|
|
62
|
+
fd: IO[Any]
|
|
63
|
+
if base_path is not None:
|
|
64
|
+
logger.debug(
|
|
65
|
+
f"Looking for data element definition file '{dataele_file}' in map_path '{base_path}'"
|
|
66
|
+
)
|
|
67
|
+
fd = open(os.path.join(base_path, dataele_file), encoding="utf-8")
|
|
68
|
+
else:
|
|
69
|
+
logger.debug(
|
|
70
|
+
f"Looking for data element definition file '{dataele_file}' in package resources"
|
|
71
|
+
)
|
|
72
|
+
fd = _res_files("pyx12").joinpath("map", dataele_file).open("rb")
|
|
73
|
+
with fd:
|
|
74
|
+
parser = et.XMLParser(encoding="utf-8")
|
|
75
|
+
for eElem in et.parse(fd, parser=parser).iter("data_ele"):
|
|
76
|
+
ele_num = eElem.get("ele_num")
|
|
77
|
+
self.dataele[ele_num] = {
|
|
78
|
+
"data_type": eElem.get("data_type"),
|
|
79
|
+
"min_len": int(eElem.get("min_len")),
|
|
80
|
+
"max_len": int(eElem.get("max_len")),
|
|
81
|
+
"name": eElem.get("name"),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
def get_by_elem_num(self, ele_num: str | None) -> _DataEle:
|
|
85
|
+
"""
|
|
86
|
+
Get the element characteristics for the indexed element code
|
|
87
|
+
:param ele_num: the data element code
|
|
88
|
+
:type ele_num: string
|
|
89
|
+
:return: {data_type, min_len, max_len, name}
|
|
90
|
+
:rtype: dict
|
|
91
|
+
"""
|
|
92
|
+
if not ele_num:
|
|
93
|
+
raise EngineError(f"Bad data element {ele_num!r}")
|
|
94
|
+
if ele_num not in self.dataele:
|
|
95
|
+
raise EngineError(f'Data Element "{ele_num}" is not defined')
|
|
96
|
+
return self.dataele[ele_num]
|
|
97
|
+
|
|
98
|
+
def debug_print(self) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Debug print data elements
|
|
101
|
+
"""
|
|
102
|
+
for ele_num in self.dataele:
|
|
103
|
+
print(self.dataele[ele_num])
|
pyx12/decorators.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import collections.abc
|
|
4
|
+
import functools
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
# See https://wiki.python.org/moin/PythonDecoratorLibrary
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def dump_args(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
12
|
+
"This decorator dumps out the arguments passed to a function before calling it"
|
|
13
|
+
argnames = func.__code__.co_varnames[: func.__code__.co_argcount]
|
|
14
|
+
fname = func.__name__
|
|
15
|
+
|
|
16
|
+
def echo_func(*args: Any, **kwargs: Any) -> Any:
|
|
17
|
+
print(
|
|
18
|
+
(
|
|
19
|
+
fname,
|
|
20
|
+
":",
|
|
21
|
+
", ".join(
|
|
22
|
+
"%s=%r" % entry for entry in list(zip(argnames, args)) + list(kwargs.items())
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
return func(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
return echo_func
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def memoize(obj: Callable[..., Any]) -> Callable[..., Any]:
|
|
32
|
+
cache: dict[Any, Any] = {}
|
|
33
|
+
obj.cache = cache # type: ignore[attr-defined]
|
|
34
|
+
|
|
35
|
+
@functools.wraps(obj)
|
|
36
|
+
def memoizer(*args: Any, **kwargs: Any) -> Any:
|
|
37
|
+
if kwargs: # frozenset is used to ensure hashability
|
|
38
|
+
key: Any = (args, frozenset(kwargs.items()))
|
|
39
|
+
else:
|
|
40
|
+
key = args
|
|
41
|
+
if key not in cache:
|
|
42
|
+
cache[key] = obj(*args, **kwargs)
|
|
43
|
+
return cache[key]
|
|
44
|
+
|
|
45
|
+
return memoizer
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class memoized:
|
|
49
|
+
"""Decorator. Caches a function's return value each time it is called.
|
|
50
|
+
If called later with the same arguments, the cached value is returned
|
|
51
|
+
(not reevaluated).
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
func: Callable[..., Any]
|
|
55
|
+
cache: dict[Any, Any]
|
|
56
|
+
|
|
57
|
+
def __init__(self, func: Callable[..., Any]) -> None:
|
|
58
|
+
self.func = func
|
|
59
|
+
self.cache = {}
|
|
60
|
+
|
|
61
|
+
def __call__(self, *args: Any) -> Any:
|
|
62
|
+
if not isinstance(args, collections.abc.Hashable):
|
|
63
|
+
# uncacheable. a list, for instance.
|
|
64
|
+
# better to not cache than blow up.
|
|
65
|
+
return self.func(*args)
|
|
66
|
+
if args in self.cache:
|
|
67
|
+
return self.cache[args]
|
|
68
|
+
else:
|
|
69
|
+
value = self.func(*args)
|
|
70
|
+
self.cache[args] = value
|
|
71
|
+
return value
|
|
72
|
+
|
|
73
|
+
def __repr__(self) -> str:
|
|
74
|
+
"""Return the function's docstring."""
|
|
75
|
+
return self.func.__doc__ or ""
|
|
76
|
+
|
|
77
|
+
def __get__(self, obj: Any, objtype: Any) -> Callable[..., Any]:
|
|
78
|
+
"""Support instance methods."""
|
|
79
|
+
return functools.partial(self.__call__, obj)
|
pyx12/errh_xml.py
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
######################################################################
|
|
2
|
+
# Copyright (c)
|
|
3
|
+
# John Holland <john@zoner.org>
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This software is licensed as described in the file LICENSE.txt, which
|
|
7
|
+
# you should have received as part of this distribution.
|
|
8
|
+
#
|
|
9
|
+
######################################################################
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
Capture X12 Errors
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import os
|
|
19
|
+
import tempfile
|
|
20
|
+
from types import TracebackType
|
|
21
|
+
from typing import IO, Any, Literal
|
|
22
|
+
|
|
23
|
+
# Intrapackage imports
|
|
24
|
+
from .errors import EngineError
|
|
25
|
+
from .xmlwriter import XMLWriter
|
|
26
|
+
|
|
27
|
+
# (kind, code, message, value, src_line)
|
|
28
|
+
_ErrTuple = tuple[str, str, str, Any, int | None]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class err_handler:
|
|
32
|
+
"""
|
|
33
|
+
The interface to the error handling structures.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
logger: logging.Logger
|
|
37
|
+
filename: str
|
|
38
|
+
fd: IO[Any]
|
|
39
|
+
cur_line: int | None
|
|
40
|
+
errors: list[_ErrTuple]
|
|
41
|
+
writer: XMLWriter
|
|
42
|
+
|
|
43
|
+
def __init__(self, xml_out: str | None = None, basedir: str | None = None) -> None:
|
|
44
|
+
"""
|
|
45
|
+
:param xml_out: Output filename, if None, will dump to tempfile
|
|
46
|
+
:param basedir: working directory, where file will be created
|
|
47
|
+
"""
|
|
48
|
+
self.logger = logging.getLogger("pyx12.errh_xml")
|
|
49
|
+
if xml_out:
|
|
50
|
+
self.filename = xml_out
|
|
51
|
+
self.fd = open(xml_out, "w", encoding="utf-8")
|
|
52
|
+
else:
|
|
53
|
+
try:
|
|
54
|
+
(fdesc, self.filename) = tempfile.mkstemp(".xml", "pyx12_")
|
|
55
|
+
self.fd = os.fdopen(fdesc, "w+b")
|
|
56
|
+
except OSError:
|
|
57
|
+
(fdesc, self.filename) = tempfile.mkstemp(
|
|
58
|
+
suffix=".xml", prefix="pyx12_", dir=basedir
|
|
59
|
+
)
|
|
60
|
+
self.fd = os.fdopen(fdesc, "w+b")
|
|
61
|
+
self.cur_line = None
|
|
62
|
+
self.errors = []
|
|
63
|
+
if not self.fd:
|
|
64
|
+
raise EngineError("Could not open temp error xml file")
|
|
65
|
+
self.writer = XMLWriter(self.fd) # type: ignore[arg-type]
|
|
66
|
+
self.writer.push("x12err")
|
|
67
|
+
|
|
68
|
+
def close(self) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Flush remaining XML closing tags and close the underlying file.
|
|
71
|
+
Idempotent.
|
|
72
|
+
"""
|
|
73
|
+
while len(self.writer) > 0:
|
|
74
|
+
self.writer.pop()
|
|
75
|
+
if self.fd is not None and not self.fd.closed:
|
|
76
|
+
self.fd.close()
|
|
77
|
+
|
|
78
|
+
def __enter__(self) -> err_handler:
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
def __exit__(
|
|
82
|
+
self,
|
|
83
|
+
exc_type: type[BaseException] | None,
|
|
84
|
+
exc: BaseException | None,
|
|
85
|
+
tb: TracebackType | None,
|
|
86
|
+
) -> Literal[False]:
|
|
87
|
+
self.close()
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
def getFilename(self) -> str:
|
|
91
|
+
return self.filename
|
|
92
|
+
|
|
93
|
+
def handleErrors(self, err_list: list[_ErrTuple]) -> None:
|
|
94
|
+
"""
|
|
95
|
+
:param err_list: list of errors to apply
|
|
96
|
+
"""
|
|
97
|
+
self.errors.extend(err_list)
|
|
98
|
+
# for (err_type, err_cde, err_str, err_val, src_line) in err_list:
|
|
99
|
+
# if err_type == 'isa':
|
|
100
|
+
# self.isa_error(err_cde, err_str)
|
|
101
|
+
# elif err_type == 'gs':
|
|
102
|
+
# self.gs_error(err_cde, err_str)
|
|
103
|
+
# elif err_type == 'st':
|
|
104
|
+
# self.st_error(err_cde, err_str)
|
|
105
|
+
# elif err_type == 'seg':
|
|
106
|
+
# self.seg_error(err_cde, err_str, err_val, src_line)
|
|
107
|
+
|
|
108
|
+
def getCurLine(self) -> int | None:
|
|
109
|
+
"""
|
|
110
|
+
:return: Current file line number
|
|
111
|
+
:rtype: int
|
|
112
|
+
"""
|
|
113
|
+
return self.cur_line
|
|
114
|
+
|
|
115
|
+
def Write(self, cur_line: int) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Generate XML for the segment data and matching map node
|
|
118
|
+
|
|
119
|
+
"""
|
|
120
|
+
if len(self.errors) > 0:
|
|
121
|
+
self.writer.push("seg", attrs={"line": "%i" % (cur_line)})
|
|
122
|
+
for err_type, err_cde, err_str, err_val, src_line in self.errors:
|
|
123
|
+
self.writer.push("err", attrs={"code": err_cde})
|
|
124
|
+
# self.writer.elem(u"type", err_type)
|
|
125
|
+
# self.writer.elem(u"code", err_cde)
|
|
126
|
+
self.writer.elem("desc", err_str)
|
|
127
|
+
if err_val:
|
|
128
|
+
self.writer.elem("errval", err_val)
|
|
129
|
+
# self.writer.push(u"seg", {u'line': '%i'%(cur_line)})
|
|
130
|
+
# self.writer.elem(u'ele', seg_data.get_value('%02i' %
|
|
131
|
+
# (i+1)),
|
|
132
|
+
# attrs={u'id': child_node.id})
|
|
133
|
+
self.writer.pop() # end err
|
|
134
|
+
self.writer.pop() # end segment
|
|
135
|
+
self.errors = []
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class ErrorErrhNull(Exception):
|
|
139
|
+
"""Class for errh_null errors."""
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class errh_list:
|
|
143
|
+
"""
|
|
144
|
+
A null error object - used for testing.
|
|
145
|
+
Stores the current error in simple variables.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
logger: logging.Logger
|
|
149
|
+
errors: list[_ErrTuple]
|
|
150
|
+
cur_line: int
|
|
151
|
+
|
|
152
|
+
def __init__(self) -> None:
|
|
153
|
+
self.logger = logging.getLogger("pyx12.errh_xml")
|
|
154
|
+
# self.id = 'ROOT'
|
|
155
|
+
self.errors = []
|
|
156
|
+
# self.cur_node = self
|
|
157
|
+
self.cur_line = 0
|
|
158
|
+
# self.err_cde = None
|
|
159
|
+
# self.err_str = None
|
|
160
|
+
|
|
161
|
+
def get_errors(self) -> list[_ErrTuple]:
|
|
162
|
+
return self.errors
|
|
163
|
+
|
|
164
|
+
def reset(self) -> None:
|
|
165
|
+
self.errors = []
|
|
166
|
+
|
|
167
|
+
def get_cur_line(self) -> int:
|
|
168
|
+
"""
|
|
169
|
+
:return: Current file line number
|
|
170
|
+
:rtype: int
|
|
171
|
+
"""
|
|
172
|
+
return self.cur_line
|
|
173
|
+
|
|
174
|
+
def set_cur_line(self, cur_line: int) -> None:
|
|
175
|
+
""" """
|
|
176
|
+
self.cur_line = cur_line
|
|
177
|
+
|
|
178
|
+
# def get_id(self):
|
|
179
|
+
# """
|
|
180
|
+
# :return: Error node type
|
|
181
|
+
# :rtype: string
|
|
182
|
+
# """
|
|
183
|
+
# return self.id
|
|
184
|
+
|
|
185
|
+
def add_isa_loop(self, seg: Any, src: Any) -> None:
|
|
186
|
+
""" """
|
|
187
|
+
# raise ErrorErrhNull, 'add_isa loop'
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
def add_gs_loop(self, seg: Any, src: Any) -> None:
|
|
191
|
+
""" """
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
def add_st_loop(self, seg: Any, src: Any) -> None:
|
|
195
|
+
""" """
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
def add_seg(
|
|
199
|
+
self, map_node: Any, seg: Any, seg_count: int, cur_line: int, ls_id: str | None
|
|
200
|
+
) -> None:
|
|
201
|
+
""" """
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
def add_ele(self, map_node: Any) -> None:
|
|
205
|
+
""" """
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
def isa_error(self, err_cde: str, err_str: str) -> None:
|
|
209
|
+
"""
|
|
210
|
+
:param err_cde: ISA level error code
|
|
211
|
+
:type err_cde: string
|
|
212
|
+
:param err_str: Description of the error
|
|
213
|
+
:type err_str: string
|
|
214
|
+
"""
|
|
215
|
+
self.errors.append(("isa", err_cde, err_str, None, None))
|
|
216
|
+
sout = ""
|
|
217
|
+
sout += "Line:%i " % (self.cur_line)
|
|
218
|
+
sout += "ISA:%s - %s" % (err_cde, err_str)
|
|
219
|
+
self.logger.error(sout)
|
|
220
|
+
|
|
221
|
+
def gs_error(self, err_cde: str, err_str: str) -> None:
|
|
222
|
+
"""
|
|
223
|
+
:param err_cde: GS level error code
|
|
224
|
+
:type err_cde: string
|
|
225
|
+
:param err_str: Description of the error
|
|
226
|
+
:type err_str: string
|
|
227
|
+
"""
|
|
228
|
+
self.errors.append(("gs", err_cde, err_str, None, None))
|
|
229
|
+
sout = ""
|
|
230
|
+
sout += "Line:%i " % (self.cur_line)
|
|
231
|
+
sout += "GS:%s - %s" % (err_cde, err_str)
|
|
232
|
+
self.logger.error(sout)
|
|
233
|
+
|
|
234
|
+
def st_error(self, err_cde: str, err_str: str) -> None:
|
|
235
|
+
"""
|
|
236
|
+
:param err_cde: Segment level error code
|
|
237
|
+
:type err_cde: string
|
|
238
|
+
:param err_str: Description of the error
|
|
239
|
+
:type err_str: string
|
|
240
|
+
"""
|
|
241
|
+
self.errors.append(("st", err_cde, err_str, None, None))
|
|
242
|
+
sout = ""
|
|
243
|
+
sout += "Line:%i " % (self.cur_line)
|
|
244
|
+
sout += "ST:%s - %s" % (err_cde, err_str)
|
|
245
|
+
self.logger.error(sout)
|
|
246
|
+
|
|
247
|
+
def seg_error(
|
|
248
|
+
self,
|
|
249
|
+
err_cde: str,
|
|
250
|
+
err_str: str,
|
|
251
|
+
err_value: str | None = None,
|
|
252
|
+
src_line: int | None = None,
|
|
253
|
+
) -> None:
|
|
254
|
+
"""
|
|
255
|
+
:param err_cde: Segment level error code
|
|
256
|
+
:type err_cde: string
|
|
257
|
+
:param err_str: Description of the error
|
|
258
|
+
:type err_str: string
|
|
259
|
+
"""
|
|
260
|
+
self.errors.append(("seg", err_cde, err_str, err_value, src_line))
|
|
261
|
+
sout = ""
|
|
262
|
+
sout += "Line:%i " % (self.cur_line)
|
|
263
|
+
sout += "SEG:%s - %s" % (err_cde, err_str)
|
|
264
|
+
if err_value:
|
|
265
|
+
sout += " (%s)" % err_value
|
|
266
|
+
self.logger.error(sout)
|
|
267
|
+
|
|
268
|
+
def ele_error(self, err_cde: str, err_str: str, bad_value: str | None) -> None:
|
|
269
|
+
"""
|
|
270
|
+
:param err_cde: Element level error code
|
|
271
|
+
:type err_cde: string
|
|
272
|
+
:param err_str: Description of the error
|
|
273
|
+
:type err_str: string
|
|
274
|
+
"""
|
|
275
|
+
self.errors.append(("ele", err_cde, err_str, bad_value, None))
|
|
276
|
+
sout = ""
|
|
277
|
+
sout += "Line:%i " % (self.cur_line)
|
|
278
|
+
sout += "ELE:%s - %s" % (err_cde, err_str)
|
|
279
|
+
if bad_value:
|
|
280
|
+
sout += " (%s)" % (bad_value)
|
|
281
|
+
self.logger.error(sout)
|
|
282
|
+
|
|
283
|
+
def close_isa_loop(self, node: Any, seg: Any, src: Any) -> None:
|
|
284
|
+
""" """
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
def close_gs_loop(self, node: Any, seg: Any, src: Any) -> None:
|
|
288
|
+
""" """
|
|
289
|
+
pass
|
|
290
|
+
|
|
291
|
+
def close_st_loop(self, node: Any, seg: Any, src: Any) -> None:
|
|
292
|
+
""" """
|
|
293
|
+
pass
|
|
294
|
+
|
|
295
|
+
def find_node(self, atype: str) -> None:
|
|
296
|
+
"""
|
|
297
|
+
Find the last node of a type
|
|
298
|
+
"""
|
|
299
|
+
pass
|
|
300
|
+
|
|
301
|
+
def get_parent(self) -> None:
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
# def get_first_child(self):
|
|
305
|
+
# """
|
|
306
|
+
# """
|
|
307
|
+
# if len(self.children) > 0:
|
|
308
|
+
# return self.children[0]
|
|
309
|
+
# else:
|
|
310
|
+
# return None
|
|
311
|
+
|
|
312
|
+
def get_next_sibling(self) -> None:
|
|
313
|
+
""" """
|
|
314
|
+
return None
|
|
315
|
+
|
|
316
|
+
def get_error_count(self) -> int:
|
|
317
|
+
""" """
|
|
318
|
+
return len(self.errors)
|
|
319
|
+
|
|
320
|
+
def is_closed(self) -> bool:
|
|
321
|
+
"""
|
|
322
|
+
:rtype: boolean
|
|
323
|
+
"""
|
|
324
|
+
return True
|