Skip to content

Commit 3a9e253

Browse files
tammoippennerdoc
andauthored
Typing (#80)
* Python 3.8 is EOL; add mypy * Add typing - remove SegmentCollection, FileSourcableMixin, UNAHandlingMixin * fmt * mypy in tests * Add mypy target in makefile * Use collections directly (new in 3.9) * rm SegmentCollection from docs * rm commented returns * stricter typing * Stricter syntax_identifier * remove py3.8 pipeline * remove py3.8 from packaging_test.yml * remove py3.8 from unittests.yml * 3.10 adjustment: Optional and Union * 3.10 adjustment: f-strings * rm 3.9 from ci * add 3.13 for testing --------- Co-authored-by: Christian González <[email protected]>
1 parent 3a4c82b commit 3a9e253

25 files changed

+1386
-296
lines changed

.github/workflows/packaging_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ${{ matrix.os }}
1212
strategy:
1313
matrix:
14-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
14+
python-version: ["3.10", "3.11", "3.12", "3.13"]
1515
os: [ubuntu-latest]
1616
steps:
1717
- uses: actions/checkout@v4

.github/workflows/unittests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
runs-on: ${{ matrix.os }}
77
strategy:
88
matrix:
9-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
9+
python-version: ["3.10", "3.11", "3.12", "3.13"]
1010
os: [ubuntu-latest]
1111
steps:
1212
- uses: actions/checkout@v4

.travis.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
language: python
22
python:
3-
- 3.8
4-
- 3.9
53
- 3.10
64
- 3.11
75
- 3.12
6+
- 3.13
87
before_install:
98
- python --version
109
- pip install -U pip

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ install:
77
build: dev
88
python -m build
99

10-
1110
upload: build
1211
twine upload dist/*
1312

1413
test:
1514
pytest --ignore tests/test_huge_message.py
1615

16+
mypy:
17+
mypy --pretty pydifact
18+
1719
test-extended:
1820
pytest
19-

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ interchange = Interchange.from_str(
4949
)
5050
for message in interchange.get_messages():
5151
for segment in message.segments:
52-
print("Segment tag: {}, content: {}".format(segment.tag, segment.elements))
52+
print(f"Segment tag: {segment.tag}, content: {segment.elements}")
5353
```
5454

5555
You may also want to iterate directly on segments :
@@ -68,7 +68,7 @@ interchange = Interchange.from_str(
6868
)
6969

7070
for segment in interchange.segments:
71-
print("Segment tag: {}, content: {}".format(segment.tag, segment.elements))
71+
print(f"Segment tag: {segment.tags}, content: {segment.elements}")
7272
```
7373

7474
Or you can create an EDI interchange on the fly:
@@ -100,7 +100,7 @@ from pydifact.segmentcollection import RawSegmentCollection
100100
collection = RawSegmentCollection.from_str("UNH+1+ORDERS:D:96A:UN:EAN008'")
101101

102102
for segment in collection.segments:
103-
print("Segment tag: {}, content: {}".format(segment.tag, segment.elements))
103+
print(f"Segment tag: {segment.tags}, content: {segment.elements}")
104104
```
105105

106106

@@ -124,7 +124,7 @@ To develop pydifact, clone the repository and install the dev requirements:
124124

125125
```bash
126126
make dev
127-
# or
127+
# or
128128
# pip install -e .[dev]
129129
```
130130

TODO.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,4 @@
22

33
* Translation of following files:
44
* Parser, ParserTest
5-
* Message, MessageTest
6-
* When Python 3.10 is adapted:
7-
* replace Union[x,y] with x|y and Optional[x] with x|None in annotations
8-
* use f-strings instead of .format-strings
5+
* Message, MessageTest

docs/API.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
API
22
===
33

4-
SegmentCollection
5-
------------------
6-
.. automodule:: pydifact.segmentcollection
7-
:members:
84

95
Parser
106
------

pydifact/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
__version__ = "0.1.8"
2626

2727
from .control.characters import Characters
28-
from .segmentcollection import SegmentCollection
2928
from .parser import Parser
3029
from .segments import Segment
3130
from .serializer import Serializer

pydifact/control/characters.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,12 @@ def with_control_character(self, cc_type: str, char: str):
8282
# set the attribute dynamically.
8383
if not hasattr(self, cc_type):
8484
raise AttributeError(
85-
"{} doesn't have an attribute with the name '{}'".format(self, cc_type)
85+
f"{self} doesn't have an attribute with the name '{cc_type}'"
8686
)
8787

8888
other = copy(self)
8989
setattr(other, cc_type, char)
9090

91-
# return clone
9291
return other
9392

9493
@property
@@ -115,10 +114,10 @@ def __str__(self) -> str:
115114
)
116115

117116
def __repr__(self):
118-
return "'{}'".format(self.__str__())
117+
return f"'{self.__str__()}'"
119118

120119
def __eq__(self, other):
121-
if type(other) == str:
120+
if isinstance(other, str):
122121
other = Characters.from_str(other)
123122
return (
124123
(self.component_separator == other.component_separator)

pydifact/parser.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2121
# THE SOFTWARE.
22-
from typing import Optional, Generator, Any
22+
from collections.abc import Iterator
2323

2424
from pydifact.tokenizer import Tokenizer
2525
from pydifact.token import Token
26-
from pydifact.segments import Segment, SegmentFactory
26+
from pydifact.segments import Element, Elements, Segment, SegmentFactory
2727
from pydifact.control import Characters
2828

2929

@@ -32,15 +32,15 @@ class Parser:
3232

3333
def __init__(
3434
self,
35-
factory: Optional[SegmentFactory] = None,
36-
characters: Optional[Characters] = None,
37-
):
35+
factory: SegmentFactory | None = None,
36+
characters: Characters | None = None,
37+
) -> None:
3838
self.factory = factory or SegmentFactory()
3939
self.characters = characters or Characters()
4040

4141
def parse(
42-
self, message: str, characters: Characters = None
43-
) -> Generator[Segment, Any, None]:
42+
self, message: str, characters: Characters | None = None
43+
) -> Iterator[Segment]:
4444
"""Parse the message into a list of segments.
4545
4646
:param characters: the control characters to use, if there is no
@@ -87,8 +87,8 @@ def parse(
8787

8888
@staticmethod
8989
def get_control_characters(
90-
message: str, characters: Characters = None
91-
) -> Optional[Characters]:
90+
message: str, characters: Characters | None = None
91+
) -> Characters | None:
9292
"""Read the UNA segment from the passed string and extract/store the control characters from it.
9393
9494
:param message: a valid EDI message string, or UNA segment string,
@@ -112,7 +112,6 @@ def get_control_characters(
112112

113113
# Get the character definitions
114114
chars = message[3:9]
115-
characters.is_extracted_from_message = True
116115

117116
characters.component_separator = chars[0]
118117
characters.data_separator = chars[1]
@@ -124,8 +123,8 @@ def get_control_characters(
124123
return characters
125124

126125
def convert_tokens_to_segments(
127-
self, tokens: list, characters: Characters, with_una: bool = False
128-
):
126+
self, tokens: Iterator[Token], characters: Characters, with_una: bool = False
127+
) -> Iterator[Segment]:
129128
"""Convert the tokenized message into an array of segments.
130129
:param tokens: The tokens that make up the message
131130
:param characters: the control characters to use
@@ -134,9 +133,10 @@ def convert_tokens_to_segments(
134133
:rtype list of Segment
135134
"""
136135

137-
segments = []
138-
current_segment = []
139-
data_element = None
136+
segments: list[Elements] = []
137+
current_segment: Elements = []
138+
data_element: list[str] = []
139+
data_element_value: Element
140140
in_segment = False
141141
empty_component_counter = 0
142142

@@ -149,12 +149,14 @@ def convert_tokens_to_segments(
149149
if token.type == Token.Type.TERMINATOR:
150150
in_segment = False
151151
if len(data_element) == 0: # empty element
152-
data_element = ""
153-
if len(data_element) == 1:
152+
data_element_value = ""
153+
elif len(data_element) == 1:
154154
# use a str instead of a list
155-
data_element = data_element[0]
155+
data_element_value = data_element[0]
156+
else:
157+
data_element_value = data_element
156158

157-
current_segment.append(data_element)
159+
current_segment.append(data_element_value)
158160
data_element = []
159161
continue
160162

@@ -174,11 +176,13 @@ def convert_tokens_to_segments(
174176
# data_element to an empty list []
175177
if token.type == Token.Type.DATA_SEPARATOR:
176178
if len(data_element) == 0: # empty element
177-
data_element = ""
179+
data_element_value = ""
178180
elif len(data_element) == 1:
179-
data_element = data_element[0]
181+
data_element_value = data_element[0]
182+
else:
183+
data_element_value = data_element
180184

181-
current_segment.append(data_element)
185+
current_segment.append(data_element_value)
182186

183187
data_element = []
184188
empty_component_counter = 0
@@ -210,4 +214,5 @@ def convert_tokens_to_segments(
210214

211215
for segment in segments:
212216
name = segment.pop(0)
213-
yield self.factory.create_segment(name, *segment)
217+
# create_segment tests name type
218+
yield self.factory.create_segment(name, *segment) # type: ignore

0 commit comments

Comments
 (0)