From f0f86f0079aa14b25efaa3c9f3975445db7427a1 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 14:34:11 +0000 Subject: [PATCH 01/42] [feat] add read_bytes() and write_bytes() methods This is required for bitfields to be able to read or write an arbitrary number of bytes from/to the stream. --- opendis/DataInputStream.py | 3 +++ opendis/DataOutputStream.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/opendis/DataInputStream.py b/opendis/DataInputStream.py index 4374410..e7cb57c 100644 --- a/opendis/DataInputStream.py +++ b/opendis/DataInputStream.py @@ -61,3 +61,6 @@ def read_int(self) -> int32: def read_unsigned_int(self) -> uint32: return struct.unpack('>I', self.stream.read(4))[0] + + def read_bytes(self, n: int) -> bytes: + return self.stream.read(n) diff --git a/opendis/DataOutputStream.py b/opendis/DataOutputStream.py index 2fe1710..95d2fc4 100644 --- a/opendis/DataOutputStream.py +++ b/opendis/DataOutputStream.py @@ -49,3 +49,6 @@ def write_int(self, val: int) -> None: def write_unsigned_int(self, val: int) -> None: self.stream.write(struct.pack('>I', val)) + def write_bytes(self, val: bytes) -> None: + self.stream.write(val) + From 738382d2c363b520915dc7178d30ef51a889d6e4 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 14:34:54 +0000 Subject: [PATCH 02/42] [refactor] add specific non-octet-size type aliases for bitfields This might change in future --- opendis/types.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/opendis/types.py b/opendis/types.py index 8e825a2..9b5b4f9 100644 --- a/opendis/types.py +++ b/opendis/types.py @@ -27,3 +27,8 @@ struct32 = bytes char8 = str char16 = str + +# Non-octet-size types for bitfields +bf_enum = int +bf_int = int +bf_uint = int From c0fb49507e43ce2e781a311a0e7d53bbd9f6a798 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 14:36:45 +0000 Subject: [PATCH 03/42] [feat] bitfield factory function, NetId and SpreadSpectrum record classes These two classes require the use of bitfields, which until now has not been implemented. The interface and design of these two classes is not yet final. Discussion and testing of this interface is recommended before finalization and further work on other bitfield records. --- opendis/record.py | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 opendis/record.py diff --git a/opendis/record.py b/opendis/record.py new file mode 100644 index 0000000..373d622 --- /dev/null +++ b/opendis/record.py @@ -0,0 +1,137 @@ +from collections.abc import Sequence +from ctypes import _CData, BigEndianStructure, c_uint +from .types import ( + bf_enum, + bf_int, + bf_uint, +) + +from DataInputStream import DataInputStream +from DataOutputStream import DataOutputStream + + +def bitfield( + name: str, + bytesize: int, + fields: Sequence[ + tuple[str, type[_CData]] | tuple[str, type[_CData], int] + ], + ): + """Factory function for bitfield structs, which are subclasses of + ctypes.Structure. + These are used in records that require them to unpack non-octet-sized fields. + """ + assert bytesize > 0, "Cannot create bitfield with zero bytes" + + class Bitfield(BigEndianStructure): + _fields_ = fields + + @staticmethod + def marshalledSize() -> int: + return bytesize + + def serialize(self, outputStream: DataOutputStream) -> None: + outputStream.write_bytes(bytes(self)) + + @classmethod + def parse(cls, inputStream: DataInputStream) -> "Bitfield": + return cls.from_buffer_copy(inputStream.read_bytes(bytesize)) + Bitfield.__name__ = name + return Bitfield + + +class NetId: + """Annex C, Table C.5 + + Represents an Operational Net in the format of NXX.XYY, where: + N = Mode + XXX = Net Number + YY = Frequency Table + """ + _struct = bitfield("NetId", 2, [ + ("netNumber", c_uint, 10), + ("frequencyTable", c_uint, 2), + ("mode", c_uint, 2), + ("padding", c_uint, 2) + ]) + + def __init__(self, + netNumber: bf_uint, + frequencyTable: bf_enum, # [UID 299] + mode: bf_enum, # [UID 298] + padding: bf_uint = 0): + # Net number ranging from 0 to 999 decimal + self.netNumber = netNumber + self.frequencyTable = frequencyTable + self.mode = mode + self.padding = padding + + def marshalledSize(self) -> int: + return self._struct.marshalledSize() + + def serialize(self, outputStream: DataOutputStream) -> None: + self._struct( + self.netNumber, + self.frequencyTable, + self.mode, + self.padding + ).serialize(outputStream) + + def parse(self, inputStream: DataInputStream) -> None: + record_bitfield = self._struct.parse(inputStream) + self.netNumber = record_bitfield.netNumber + self.frequencyTable = record_bitfield.frequencyTable + self.mode = record_bitfield.mode + self.padding = record_bitfield.padding + + +class SpreadSpectrum: + """6.2.59 Modulation Type Record, Table 90 + + Modulation used for radio transmission is characterized in a generic + fashion by the Spread Spectrum, Major Modulation, and Detail fields. + + Each independent type of spread spectrum technique shall be represented by + a single element of this array. + If a particular spread spectrum technique is in use, the corresponding array + element shall be set to one; otherwise it shall be set to zero. + All unused array elements shall be set to zero. + + In Python, the presence or absence of each technique is indicated by a bool. + """ + _struct = bitfield("SpreadSpectrum", 2, [ + ("frequencyHopping", c_uint, 1), + ("pseudoNoise", c_uint, 1), + ("timeHopping", c_uint, 1), + ("padding", c_uint, 13) + ]) + + def __init__(self, + frequencyHopping: bool, + pseudoNoise: bool, + timeHopping: bool, + padding: bf_uint = 0): + self.frequencyHopping = frequencyHopping + self.pseudoNoise = pseudoNoise + self.timeHopping = timeHopping + self.padding = padding + + def marshalledSize(self) -> int: + return self._struct.marshalledSize() + + def serialize(self, outputStream: DataOutputStream) -> None: + # Bitfield expects int input + self._struct( + int(self.frequencyHopping), + int(self.pseudoNoise), + int(self.timeHopping), + self.padding + ).serialize(outputStream) + + def parse(self, inputStream: DataInputStream) -> None: + # Pass bool to __init__ instead of int + record_bitfield = self._struct.parse(inputStream) + self.frequencyHopping = bool(record_bitfield.frequencyHopping) + self.pseudoNoise = bool(record_bitfield.pseudoNoise) + self.timeHopping = bool(record_bitfield.timeHopping) + From 346b8bf21859db23b004fd65ce292a3b751b5c7f Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 14:38:13 +0000 Subject: [PATCH 04/42] [docs] TransmitterPdu comments cleanup --- opendis/dis7.py | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index db44079..493dbb0 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -5412,48 +5412,29 @@ def __init__(self, antennaPatternList=None): super(TransmitterPdu, self).__init__() self.radioReferenceID = radioReferenceID or EntityID() - """ID of the entitythat is the source of the communication""" + """ID of the entity that is the source of the communication""" self.radioNumber = radioNumber """particular radio within an entity""" self.radioEntityType = radioEntityType or EntityType() # TODO: validation - """Type of radio""" self.transmitState = transmitState - """transmit state""" self.inputSource = inputSource - """input source""" self.variableTransmitterParameterCount = variableTransmitterParameterCount - """count field""" self.antennaLocation = antennaLocation or Vector3Double() - """Location of antenna""" self.relativeAntennaLocation = relativeAntennaLocation or Vector3Float( ) - """relative location of antenna""" self.antennaPatternType = antennaPatternType - """antenna pattern type""" self.antennaPatternCount = antennaPatternCount - """antenna pattern length""" self.frequency = frequency - """frequency""" self.transmitFrequencyBandwidth = transmitFrequencyBandwidth - """transmit frequency Bandwidth""" self.power = power - """transmission power""" self.modulationType = modulationType or ModulationType() - """modulation""" self.cryptoSystem = cryptoSystem - """crypto system enumeration""" self.cryptoKeyId = cryptoKeyId - """crypto system key identifer""" self.modulationParameterCount = modulationParameterCount - """how many modulation parameters we have""" self.padding2 = 0 - """padding2""" self.padding3 = 0 - """padding3""" self.modulationParametersList = modulationParametersList or [] - """variable length list of modulation parameters""" self.antennaPatternList = antennaPatternList or [] - """variable length list of antenna pattern records""" # TODO: zero or more Variable Transmitter Parameters records (see 6.2.95) def serialize(self, outputStream): From 1b7f07182ad5734596fd42a7523b3b67502cc7f7 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 14:41:04 +0000 Subject: [PATCH 05/42] [refactor] Add default values to allow init without initial values This follows the pattern established by the classes in dis7.py --- opendis/record.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/opendis/record.py b/opendis/record.py index 373d622..5c6011d 100644 --- a/opendis/record.py +++ b/opendis/record.py @@ -56,9 +56,9 @@ class NetId: ]) def __init__(self, - netNumber: bf_uint, - frequencyTable: bf_enum, # [UID 299] - mode: bf_enum, # [UID 298] + netNumber: bf_uint = 0, + frequencyTable: bf_enum = 0, # [UID 299] + mode: bf_enum = 0, # [UID 298] padding: bf_uint = 0): # Net number ranging from 0 to 999 decimal self.netNumber = netNumber @@ -107,9 +107,9 @@ class SpreadSpectrum: ]) def __init__(self, - frequencyHopping: bool, - pseudoNoise: bool, - timeHopping: bool, + frequencyHopping: bool = False, + pseudoNoise: bool = False, + timeHopping: bool = False, padding: bf_uint = 0): self.frequencyHopping = frequencyHopping self.pseudoNoise = pseudoNoise From bff4aa3bf2daf755c1435744161fa4ade4840740 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 14:42:47 +0000 Subject: [PATCH 06/42] [refactor] Use SpreadSpectrum in ModulationType Initial test for bitfield class --- opendis/dis7.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index 493dbb0..e4a150f 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -6,8 +6,6 @@ enum8, enum16, enum32, - int8, - int16, int32, uint8, uint16, @@ -19,6 +17,7 @@ struct16, struct32, ) +from .record import SpreadSpectrum class DataQueryDatumSpecification: @@ -2020,11 +2019,11 @@ class ModulationType: """ def __init__(self, - spreadSpectrum: struct16 = 0, # See RPR Enumerations + spreadSpectrum: SpreadSpectrum | None = None, # See RPR Enumerations majorModulation: enum16 = 0, # [UID 155] - detail: enum16 =0, # [UID 156-162] - radioSystem: enum16 =0): # [UID 163] - self.spreadSpectrum = spreadSpectrum + detail: enum16 = 0, # [UID 156-162] + radioSystem: enum16 = 0): # [UID 163] + self.spreadSpectrum = spreadSpectrum or SpreadSpectrum() """This field shall indicate the spread spectrum technique or combination of spread spectrum techniques in use. Bit field. 0=freq hopping, 1=psuedo noise, time hopping=2, reamining bits unused""" self.majorModulation = majorModulation """the major classification of the modulation type.""" From a86ae5c6284d8cc5133ea44cdafdd3f04d7ade22 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 14:51:37 +0000 Subject: [PATCH 07/42] [fix] bugfixes --- opendis/record.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/opendis/record.py b/opendis/record.py index 5c6011d..670daa3 100644 --- a/opendis/record.py +++ b/opendis/record.py @@ -1,25 +1,32 @@ from collections.abc import Sequence -from ctypes import _CData, BigEndianStructure, c_uint +from ctypes import c_uint, BigEndianStructure from .types import ( bf_enum, bf_int, bf_uint, ) -from DataInputStream import DataInputStream -from DataOutputStream import DataOutputStream +from .DataInputStream import DataInputStream +from .DataOutputStream import DataOutputStream def bitfield( name: str, bytesize: int, - fields: Sequence[ - tuple[str, type[_CData]] | tuple[str, type[_CData], int] - ], + fields: Sequence[tuple], ): """Factory function for bitfield structs, which are subclasses of ctypes.Structure. These are used in records that require them to unpack non-octet-sized fields. + + Arguments + --------- + name: str + The name of the bitfield struct. + bytesize: int + The number of bytes required by the bitfield, including padding. + fields: Sequence[tuple[str, ctypes._CData] | tuple[str, ctypes._CData, int]] + A sequence of field descriptions. See ctypes.Structure documentation for details. """ assert bytesize > 0, "Cannot create bitfield with zero bytes" From ba89412e126052084ecba072d916137ab18bc6cc Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 15:25:42 +0000 Subject: [PATCH 08/42] [cleanup] minor edits --- opendis/record.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/opendis/record.py b/opendis/record.py index 670daa3..275753b 100644 --- a/opendis/record.py +++ b/opendis/record.py @@ -1,11 +1,10 @@ from collections.abc import Sequence from ctypes import c_uint, BigEndianStructure + from .types import ( bf_enum, - bf_int, bf_uint, ) - from .DataInputStream import DataInputStream from .DataOutputStream import DataOutputStream @@ -32,14 +31,14 @@ def bitfield( class Bitfield(BigEndianStructure): _fields_ = fields - + @staticmethod def marshalledSize() -> int: return bytesize - + def serialize(self, outputStream: DataOutputStream) -> None: outputStream.write_bytes(bytes(self)) - + @classmethod def parse(cls, inputStream: DataInputStream) -> "Bitfield": return cls.from_buffer_copy(inputStream.read_bytes(bytesize)) @@ -61,7 +60,7 @@ class NetId: ("mode", c_uint, 2), ("padding", c_uint, 2) ]) - + def __init__(self, netNumber: bf_uint = 0, frequencyTable: bf_enum = 0, # [UID 299] @@ -112,7 +111,7 @@ class SpreadSpectrum: ("timeHopping", c_uint, 1), ("padding", c_uint, 13) ]) - + def __init__(self, frequencyHopping: bool = False, pseudoNoise: bool = False, From 1aaaa877d4479e857b3617d44c98e7d3f825f844 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 18:30:44 +0000 Subject: [PATCH 09/42] [fix] Bitfield packs into correct number of bytes The fields fall back on default size for c_uint if not specified, resulting in the bitfield packing a 16-bit bitfield into 4 bytes instead of 2. This fix induces the correct packing behaviour. --- opendis/record.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/opendis/record.py b/opendis/record.py index 275753b..77d515d 100644 --- a/opendis/record.py +++ b/opendis/record.py @@ -1,5 +1,5 @@ from collections.abc import Sequence -from ctypes import c_uint, BigEndianStructure +from ctypes import c_uint8, c_uint16, BigEndianStructure from .types import ( bf_enum, @@ -55,10 +55,10 @@ class NetId: YY = Frequency Table """ _struct = bitfield("NetId", 2, [ - ("netNumber", c_uint, 10), - ("frequencyTable", c_uint, 2), - ("mode", c_uint, 2), - ("padding", c_uint, 2) + ("netNumber", c_uint16, 10), + ("frequencyTable", c_uint8, 2), + ("mode", c_uint8, 2), + ("padding", c_uint8, 2) ]) def __init__(self, @@ -106,10 +106,10 @@ class SpreadSpectrum: In Python, the presence or absence of each technique is indicated by a bool. """ _struct = bitfield("SpreadSpectrum", 2, [ - ("frequencyHopping", c_uint, 1), - ("pseudoNoise", c_uint, 1), - ("timeHopping", c_uint, 1), - ("padding", c_uint, 13) + ("frequencyHopping", c_uint8, 1), + ("pseudoNoise", c_uint8, 1), + ("timeHopping", c_uint8, 1), + ("padding", c_uint16, 13) ]) def __init__(self, From 9acd3b826130638b270e9cec35231d05d5888e72 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 18:31:19 +0000 Subject: [PATCH 10/42] [fix] Use SpreadSpectrum to parse and serialize stream --- opendis/dis7.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index c3f549f..67a25c8 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -2039,14 +2039,14 @@ def __init__(self, def serialize(self, outputStream): """serialize the class""" - outputStream.write_unsigned_short(self.spreadSpectrum) + self.spreadSpectrum.serialize(outputStream) outputStream.write_unsigned_short(self.majorModulation) outputStream.write_unsigned_short(self.detail) outputStream.write_unsigned_short(self.radioSystem) def parse(self, inputStream): """Parse a message. This may recursively call embedded objects.""" - self.spreadSpectrum = inputStream.read_unsigned_short() + self.spreadSpectrum.parse(inputStream) self.majorModulation = inputStream.read_unsigned_short() self.detail = inputStream.read_unsigned_short() self.radioSystem = inputStream.read_unsigned_short() From d9e211c90e335aa23747e6f824c614cd84c515a7 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sun, 19 May 2024 03:39:20 +0000 Subject: [PATCH 11/42] [fix] SignalPdu correctly parses data samples This was missed in an earlier refactor that converted length/count attributes into properties. --- opendis/dis7.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index 67a25c8..36b64a2 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -7459,9 +7459,11 @@ def parse(self, inputStream): self.encodingScheme = inputStream.read_unsigned_short() self.tdlType = inputStream.read_unsigned_short() self.sampleRate = inputStream.read_unsigned_int() - self.dataLength = inputStream.read_unsigned_short() + dataLength = inputStream.read_unsigned_short() + # TODO: Make validation optional + assert dataLength % 8 == 0 self.samples = inputStream.read_unsigned_short() - for idx in range(0, self.dataLength // 8): + for idx in range(0, dataLength // 8): element = inputStream.read_unsigned_byte() self.data.append(element) From d06a00b1d38122e5fb22858f9c971556acd88b88 Mon Sep 17 00:00:00 2001 From: skrista <61268124+skrsta@users.noreply.github.com> Date: Sat, 6 Jul 2024 19:14:12 +0200 Subject: [PATCH 12/42] fix regression of parsing dataLenght in SignalPdu in 98b2834 This fix work from #51 introduced in e9b6788 and regression from 98b2834 --- opendis/dis7.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index 36b64a2..4ca5472 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -7460,8 +7460,6 @@ def parse(self, inputStream): self.tdlType = inputStream.read_unsigned_short() self.sampleRate = inputStream.read_unsigned_int() dataLength = inputStream.read_unsigned_short() - # TODO: Make validation optional - assert dataLength % 8 == 0 self.samples = inputStream.read_unsigned_short() for idx in range(0, dataLength // 8): element = inputStream.read_unsigned_byte() From 546be77143a137aa9c6d850513700caca3949fda Mon Sep 17 00:00:00 2001 From: "Dr. Bryan T Weinstein" Date: Mon, 28 Jul 2025 07:37:06 -0400 Subject: [PATCH 13/42] ci: publishes library to local registry --- .gitignore | 1 - .gitlab-ci.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .gitlab-ci.yml diff --git a/.gitignore b/.gitignore index eafc1e0..db26086 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ build/* dist/* docs/* .local/* -.* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..97d189d --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,26 @@ +stages: + - publish + +default: + tags: + - accel_docker_builder_runner +# +# Publish +# +include: + # Test Publish Poetry Component + - component: $CI_SERVER_FQDN/cuic-lab/cuic-devops/cicd-templates/poetry-project-ci/publish-poetry@main + inputs: + stage: publish + runner_tag: accel_docker_builder_runner + image: devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 + + +publish:poetry_build_and_publish: + # Override the build rules for the include + rules: + - if: $CI_COMMIT_TAG # publish on tag + when: on_success + variables: + SDK_VERSION: $CI_COMMIT_TAG + - when: never \ No newline at end of file From 08ffdb8ddbc2b0781a83b8cdd66aefa520ec43fa Mon Sep 17 00:00:00 2001 From: "Dr. Bryan T Weinstein" Date: Mon, 28 Jul 2025 08:00:40 -0400 Subject: [PATCH 14/42] feat: allow a newer version of numpy --- poetry.lock | 9 +++++---- pyproject.toml | 9 ++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index ff04cdd..cc1b4cd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "numpy" @@ -6,6 +6,7 @@ version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, @@ -46,6 +47,6 @@ files = [ ] [metadata] -lock-version = "2.0" -python-versions = "^3.9" -content-hash = "73104148745e647961282501b5fbe50c548b7829b9369051b6f32c8ba02cad44" +lock-version = "2.1" +python-versions = ">=3.9,<4.0" +content-hash = "1c4e764d8ec0f1b9aebe1ccf189bad3e2a816dfc027032f60036c128506021de" diff --git a/pyproject.toml b/pyproject.toml index 5af6948..a0f1287 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "opendis" -version = "1.0" +version = "1.0.1" description='implementation of DIS, IEEE-1278.1' authors = ["Don McGregor "] readme = "README.md" @@ -14,10 +14,9 @@ classifiers=[ ] [tool.poetry.dependencies] -python = "^3.9" -numpy = "^1.26.4" - +python = ">=3.9,<4.0" +numpy = ">=1.26.4,<3.0" [build-system] -requires = ["poetry-core"] +requires = ["poetry-core>=2.0.0"] build-backend = "poetry.core.masonry.api" From 8949fdcb5179ed7beda2872b2297fe8b6c937a92 Mon Sep 17 00:00:00 2001 From: "Dr. Bryan T Weinstein" Date: Mon, 28 Jul 2025 08:12:18 -0400 Subject: [PATCH 15/42] feat: alpha releases now indicate accel opendis releases --- .gitlab-ci.yml | 4 +--- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 97d189d..db88e12 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,8 +19,6 @@ include: publish:poetry_build_and_publish: # Override the build rules for the include rules: - - if: $CI_COMMIT_TAG # publish on tag + - if: '$CI_COMMIT_BRANCH == "master"' when: on_success - variables: - SDK_VERSION: $CI_COMMIT_TAG - when: never \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index a0f1287..143fdf7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "opendis" -version = "1.0.1" +version = "1.1.0a" description='implementation of DIS, IEEE-1278.1' authors = ["Don McGregor "] readme = "README.md" From 57abd22448f7e2fd2795ecad7cfa6fe5198c12f9 Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Mon, 28 Jul 2025 13:37:29 -0400 Subject: [PATCH 16/42] ci: added cz autobump --- .gitlab-ci.yml | 35 +- ci/auto-bump.sh | 22 ++ poetry.lock | 867 +++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 35 +- 4 files changed, 951 insertions(+), 8 deletions(-) create mode 100755 ci/auto-bump.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index db88e12..71cef03 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,40 @@ +variables: + PROJECT_REGISTRY: ${PROJECT_REGISTRY} + NAMESPACE: ${NAMESPACE} + APP_NAME: "accel-api" + GIT_USER: "Accel Gitlab Runner" + GIT_EMAIL: "runner@accel.mitre.org" + CURRENT_USER: "None" + GIT_STRATEGY: clone + stages: + - version - publish default: tags: - accel_docker_builder_runner + +# +# Version +# +auto-bump-dev: + stage: version + image: devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 + script: + - ./ci/auto-bump.sh + rules: + - if: $CI_COMMIT_TAG + when: never + - if: $CI_ONLY_CHORES == "true" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE =~ /^bump:/ + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + when: on_success + + + # # Publish # @@ -19,6 +50,6 @@ include: publish:poetry_build_and_publish: # Override the build rules for the include rules: - - if: '$CI_COMMIT_BRANCH == "master"' + - if: $CI_COMMIT_TAG # publish on tag when: on_success - - when: never \ No newline at end of file + - when: never diff --git a/ci/auto-bump.sh b/ci/auto-bump.sh new file mode 100755 index 0000000..d42640a --- /dev/null +++ b/ci/auto-bump.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -o errexit + +git config --global user.name "$GIT_USER" +git config --global user.email "$GIT_EMAIL" +git config http.https://gitlab.mitre.org.sslcainfo /etc/ssl/certs/ca-certificates.crt +git remote set-url origin \ + "https://gitlab-ci-token:${GITLAB_CI_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" + +echo $(git remote get-url origin) + +exists=`git show-ref refs/heads/main` && if [ -n "$exists" ]; then git branch -D main; fi +git checkout -b main + +git status + +echo "Auto Bumping Repo" +cz bump --changelog --check-consistency --yes --no-verify --prerelease alpha + +git status +git push origin main:$CI_COMMIT_REF_SLUG --tag -f diff --git a/poetry.lock b/poetry.lock index cc1b4cd..f90c3b2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,556 @@ # This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +[[package]] +name = "argcomplete" +version = "3.6.2" +description = "Bash tab completion for argparse" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, + {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, +] + +[package.extras] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] + +[[package]] +name = "attrs" +version = "25.3.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, + {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, +] + +[package.extras] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] + +[[package]] +name = "black" +version = "25.1.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, + {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, + {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, + {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, + {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, + {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, + {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, + {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, + {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, + {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, + {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, + {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, + {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, + {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, + {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, + {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, + {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, + {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, + {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, + {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, + {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, + {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, +] + +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version == \"3.9\"" +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "click" +version = "8.2.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_version != \"3.9\"" +files = [ + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "commitizen" +version = "4.8.3" +description = "Python commitizen client tool" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["dev"] +files = [ + {file = "commitizen-4.8.3-py3-none-any.whl", hash = "sha256:91f261387ca2bbb4ab6c79a1a6378dc1576ffb40e3b7dbee201724d95aceba38"}, + {file = "commitizen-4.8.3.tar.gz", hash = "sha256:303ebdc271217aadbb6a73a015612121291d180c8cdd05b5251c7923d4a14195"}, +] + +[package.dependencies] +argcomplete = ">=1.12.1,<3.7" +charset-normalizer = ">=2.1.0,<4" +colorama = ">=0.4.1,<1.0" +decli = ">=0.6.0,<1.0" +importlib-metadata = [ + {version = ">=8.0.0,<8.7.0 || >8.7.0,<9.0.0", markers = "python_version == \"3.9\""}, + {version = ">=8.0.0,<9.0.0", markers = "python_version != \"3.9\""}, +] +jinja2 = ">=2.10.3" +packaging = ">=19" +pyyaml = ">=3.08" +questionary = ">=2.0,<3.0" +termcolor = ">=1.1.0,<4.0.0" +tomlkit = ">=0.5.3,<1.0.0" +typing-extensions = {version = ">=4.0.1,<5.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "decli" +version = "0.6.3" +description = "Minimal, easy-to-use, declarative cli tool" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "decli-0.6.3-py3-none-any.whl", hash = "sha256:5152347c7bb8e3114ad65db719e5709b28d7f7f45bdb709f70167925e55640f3"}, + {file = "decli-0.6.3.tar.gz", hash = "sha256:87f9d39361adf7f16b9ca6e3b614badf7519da13092f2db3c80ca223c53c7656"}, +] + +[[package]] +name = "docformatter" +version = "1.7.7" +description = "Formats docstrings to follow PEP 257" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["dev"] +files = [ + {file = "docformatter-1.7.7-py3-none-any.whl", hash = "sha256:7af49f8a46346a77858f6651f431b882c503c2f4442c8b4524b920c863277834"}, + {file = "docformatter-1.7.7.tar.gz", hash = "sha256:ea0e1e8867e5af468dfc3f9e947b92230a55be9ec17cd1609556387bffac7978"}, +] + +[package.dependencies] +charset_normalizer = ">=3.0.0,<4.0.0" +tomli = {version = ">=2.0.0,<3.0.0", optional = true, markers = "python_version < \"3.11\" and extra == \"tomli\""} +untokenize = ">=0.1.1,<0.2.0" + +[package.extras] +tomli = ["tomli (>=2.0.0,<3.0.0) ; python_version < \"3.11\""] + +[[package]] +name = "flake8" +version = "7.3.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, + {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.14.0,<2.15.0" +pyflakes = ">=3.4.0,<3.5.0" + +[[package]] +name = "flake8-bugbear" +version = "24.12.12" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +optional = false +python-versions = ">=3.8.1" +groups = ["dev"] +files = [ + {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"}, + {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +flake8 = ">=6.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-docstrings-complete" +version = "1.4.1" +description = "A linter that checks docstrings are complete" +optional = false +python-versions = "<4.0.0,>=3.9.0" +groups = ["dev"] +files = [ + {file = "flake8_docstrings_complete-1.4.1-py3-none-any.whl", hash = "sha256:c315534455b2981e9c4ef21904dd35b7c4f4bf65814f726ac01a381786fc4eea"}, + {file = "flake8_docstrings_complete-1.4.1.tar.gz", hash = "sha256:43bf88e34e3029fc8b38b4e3ccb5966bd07ef63d94cd3943629d1be88cd72553"}, +] + +[package.dependencies] +flake8 = ">=5" + +[[package]] +name = "flake8-print" +version = "5.0.0" +description = "print statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "flake8-print-5.0.0.tar.gz", hash = "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9"}, + {file = "flake8_print-5.0.0-py3-none-any.whl", hash = "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "importlib-metadata" +version = "8.6.1" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version == \"3.9\"" +files = [ + {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, + {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version != \"3.9\"" +files = [ + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "isort" +version = "6.0.1" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.9.0" +groups = ["dev"] +files = [ + {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, + {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, +] + +[package.extras] +colors = ["colorama"] +plugins = ["setuptools"] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "3.0.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + [[package]] name = "numpy" version = "1.26.4" @@ -46,7 +597,321 @@ files = [ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, + {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.51" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, + {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "pycodestyle" +version = "2.14.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, + {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3) ; python_version < \"3.11\""] + +[[package]] +name = "pyflakes" +version = "3.4.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, + {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "questionary" +version = "2.1.0" +description = "Python library to build pretty command line user prompts ⭐️" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, + {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, +] + +[package.dependencies] +prompt_toolkit = ">=2.0,<4.0" + +[[package]] +name = "snowballstemmer" +version = "3.0.1" +description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" +groups = ["dev"] +files = [ + {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, + {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, +] + +[[package]] +name = "termcolor" +version = "3.1.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa"}, + {file = "termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.2.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.11\"" +files = [ + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, +] + +[[package]] +name = "tomlkit" +version = "0.13.3" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, + {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, +] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version < \"3.11\"" +files = [ + {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, + {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, +] + +[[package]] +name = "untokenize" +version = "0.1.1" +description = "Transforms tokens into original source code (while preserving whitespace)." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "zipp" +version = "3.23.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "1c4e764d8ec0f1b9aebe1ccf189bad3e2a816dfc027032f60036c128506021de" +content-hash = "c1d1f5d82e30c2b254ed4667ceb803b380ccd93089edcdb15c58a29ec8eeb1b5" diff --git a/pyproject.toml b/pyproject.toml index 143fdf7..08115d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,12 @@ -[tool.poetry] +[project] name = "opendis" version = "1.1.0a" description='implementation of DIS, IEEE-1278.1' -authors = ["Don McGregor "] +authors = [ + {name="Don McGregor", email="mcgredo@nps.edu"}, +] readme = "README.md" +requires-python = ">=3.9,<4.0" repository = 'https://github.com/open-dis/open-dis-python' documentation = "https://open-dis.org/" classifiers=[ @@ -12,10 +15,32 @@ classifiers=[ "Operating System :: OS Independent", "Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator" ] + dependencies = [ + "numpy (>=1.26.4,<3.0)", + ] + + +[tool.poetry.group.dev.dependencies] +black = ">=24.0.0" +isort = ">=5.13.2" +commitizen = ">=3.13.0" +flake8 = ">=7.0.0" +flake8-bugbear = "^24.8.0" +flake8-print = "^5.0.0" +flake8-docstrings = "^1.7.0" +flake8-docstrings-complete = "^1.3.0" +docformatter = {extras = ["tomli"], version = "^1.7.5"} -[tool.poetry.dependencies] -python = ">=3.9,<4.0" -numpy = ">=1.26.4,<3.0" + +[tool.commitizen] +version = "1.1.0a" +update_changelog_on_bump = false +changelog_merge_prerelease = false +major_version_zero = true +bump_message = "bump: $current_version → $new_version" +version_files = [ + "pyproject.toml:^version", +] [build-system] requires = ["poetry-core>=2.0.0"] From c2b9b10e34f476504f7a4e771037cd205ba562b1 Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Mon, 28 Jul 2025 15:03:33 -0400 Subject: [PATCH 17/42] ci: fixed issue with entrypoint --- .gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 71cef03..affcf81 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,7 +20,9 @@ default: # auto-bump-dev: stage: version - image: devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 + image: + name: devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 + entrypoint: [""] script: - ./ci/auto-bump.sh rules: From f6ea817f8aba9d6ef5e3f79a6b4f79ea05b7f56f Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Mon, 28 Jul 2025 15:01:22 -0400 Subject: [PATCH 18/42] ci: fixed entrypoint issue --- .gitlab-ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index affcf81..805dd6c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,9 +20,8 @@ default: # auto-bump-dev: stage: version - image: - name: devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 - entrypoint: [""] + image: devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 + entrypoint: [""] script: - ./ci/auto-bump.sh rules: From b6457b1a887043255c98f2ab8b27b377ca3f8c38 Mon Sep 17 00:00:00 2001 From: "Dr. Bryan T Weinstein" Date: Mon, 28 Jul 2025 15:44:07 -0400 Subject: [PATCH 19/42] build: adding docker support for builds --- .env | 1 + .gitlab-ci.yml | 33 ++++++++++++++++++++++++-- ci/compose.override.yml | 3 +++ ci/docker-compose.yml | 8 +++++++ docker-compose.yml | 23 ++++++++++++++++++ docker/Dockerfile | 32 +++++++++++++++++++++++++ pyproject.toml | 1 + setup | 52 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 .env create mode 100644 ci/compose.override.yml create mode 100644 ci/docker-compose.yml create mode 100644 docker-compose.yml create mode 100644 docker/Dockerfile create mode 100644 setup diff --git a/.env b/.env new file mode 100644 index 0000000..0c5e42c --- /dev/null +++ b/.env @@ -0,0 +1 @@ +version=1.1.0a \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 805dd6c..4b4b110 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,5 @@ variables: + COMPOSE_FILE: "ci/docker-compose.yml" PROJECT_REGISTRY: ${PROJECT_REGISTRY} NAMESPACE: ${NAMESPACE} APP_NAME: "accel-api" @@ -8,6 +9,7 @@ variables: GIT_STRATEGY: clone stages: + - build - version - publish @@ -15,13 +17,40 @@ default: tags: - accel_docker_builder_runner +# +# Build +# +build:build_poetry_containers: + stage: build + before_script: + - echo "$DEV_SERVER_PASSWORD" | docker login $PROJECT_REGISTRY -u $DEV_SERVER_USERNAME --password-stdin + - docker info + script: + - docker pull devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 + - docker compose build opendis + rules: + - if: $CI_COMMIT_TAG + when: never + - if: $CI_COMMIT_MESSAGE =~ /^bump:/ + when: never + - if: $CI_PIPELINE_SOURCE == "trigger" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS + when: never + - if: $CI_ONLY_CHORES == "true" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $CI_RELEASE == "true" + when: on_success + - if: $CI_COMMIT_BRANCH + # # Version # auto-bump-dev: stage: version - image: devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 - entrypoint: [""] + image: + name: devops.cuic.mitre.org/accel-lab/opendis:ci_${CI_PIPELINE_ID} script: - ./ci/auto-bump.sh rules: diff --git a/ci/compose.override.yml b/ci/compose.override.yml new file mode 100644 index 0000000..dd6e988 --- /dev/null +++ b/ci/compose.override.yml @@ -0,0 +1,3 @@ +services: + opendis: + image: devops.cuic.mitre.org/accel-lab/opendis:ci_${CI_PIPELINE_ID} \ No newline at end of file diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml new file mode 100644 index 0000000..02da167 --- /dev/null +++ b/ci/docker-compose.yml @@ -0,0 +1,8 @@ +name: opendis-ci + +include: + - path: + - ../docker-compose.yml + - compose.override.yml + env_file: + - ../.env \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..22077e3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +name: opendis + +services: + opendis: + # By default just runs tests when spun up; see entrypoint + image: devops.cuic.mitre.org/accel-lab/opendis:${version?:err} + pull_policy: build + build: + context: . + dockerfile: ./docker/Dockerfile + secrets: + - dev_server_username + - dev_server_password + container_name: opendis + volumes: + - .:/home/cuic-dev/accel-open-dis-python + - ./opendis:/usr/local/lib/python3.11/site-packages/opendis + +secrets: + dev_server_password: + environment: DEV_SERVER_PASSWORD + dev_server_username: + environment: DEV_SERVER_USERNAME diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..30f6d1a --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,32 @@ +FROM devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 + +ENV HTTP_PROXY=devops.cuic.mitre.org +ENV APP_HOME=/home/cuic-dev/accel-open-dis-python +ENV PROJECT_REGISTRY=devops.cuic.mitre.org + +WORKDIR $APP_HOME + +## Copy code into container +COPY . . + +RUN poetry config certificates.cuic-devops-registry.cert /etc/ssl/certs/ca-certificates.crt + +RUN echo `poetry --version` + +# Use the below to use secrets +RUN --mount=type=secret,id=dev_server_password \ + --mount=type=secret,id=dev_server_username \ + DEV_SERVER_USERNAME=$(cat /run/secrets/dev_server_username) \ + && export DEV_SERVER_USERNAME \ + && DEV_SERVER_PASSWORD=$(cat /run/secrets/dev_server_password) \ + && export DEV_SERVER_PASSWORD \ + && poetry config http-basic.cuic-devops-registry $DEV_SERVER_USERNAME $DEV_SERVER_PASSWORD \ + && poetry install --no-root --only main,dev + +ENTRYPOINT [ "" ] + +# Copy code into correct location into site-packages since poetry does not cooperate, even without no-root +WORKDIR /usr/local/lib/python${PYTHON_VERSION}/site-packages/opendis +COPY ./opendis . + +WORKDIR $APP_HOME diff --git a/pyproject.toml b/pyproject.toml index 08115d5..ad102df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ major_version_zero = true bump_message = "bump: $current_version → $new_version" version_files = [ "pyproject.toml:^version", + ".env:^version" ] [build-system] diff --git a/setup b/setup new file mode 100644 index 0000000..e9564ba --- /dev/null +++ b/setup @@ -0,0 +1,52 @@ +echo "SETTING CURRENT USER" +export CURRENT_USER=$(id -u):$(id -g) + +if [[ $SHELL == *"bash"* ]]; then + read -p $'Enter your CUIC Devops Server username: \n> ' DEV_SERVER_USERNAME + export DEV_SERVER_USERNAME + + read -sp $'Enter your CUIC Devops Server password: \n> ' DEV_SERVER_PASSWORD + export DEV_SERVER_PASSWORD +else + echo $'Enter your CUIC Devops Server username:' + read DEV_SERVER_USERNAME + export DEV_SERVER_USERNAME + + echo $'Enter your CUIC Devops Server password:' + stty -echo + read DEV_SERVER_PASSWORD + export DEV_SERVER_PASSWORD + stty echo +fi + +export DEV_NPM_TOKEN=`echo -n "${DEV_SERVER_USERNAME}:${DEV_SERVER_PASSWORD}" | openssl base64` + +export HTTP_PROXY=devops.cuic.mitre.org + +# Docker login +echo $DEV_SERVER_PASSWORD | docker login devops.cuic.mitre.org --username ${DEV_SERVER_USERNAME} --password-stdin + +# Poetry credentials +if ! command -v poetry &> /dev/null +then + echo "Poetry not installed. Skipping..." + return +fi +poetry config http-basic.cuic-devops-registry ${DEV_SERVER_USERNAME} ${DEV_SERVER_PASSWORD} +if [ -f "/etc/ssl/certs/ca-certificates.crt" ]; then + CERT_PATH="/etc/ssl/certs/ca-certificates.crt" +elif [[ "$OSTYPE" == "darwin"* ]]; then + OPENSSL_DIR=$(openssl version -d | cut -d'"' -f2) + CERT_PATH="${OPENSSL_DIR}/cert.pem" +fi +poetry config certificates.cuic-devops-registry.cert $CERT_PATH +poetry config repositories.cuic-devops-publish https://devops.cuic.mitre.org/nexus/repository/python-hosted/ +poetry config http-basic.cuic-devops-publish ${DEV_SERVER_USERNAME} ${DEV_SERVER_PASSWORD} + +poetry config certificates.cuic-devops-publish.cert $CERT_PATH +poetry config certificates.cuic-devops-publish.cert /etc/ssl/certs/ca-certificates.crt + +if [[ -n "${SSL_CERT_FILE}" ]]; then + poetry config certificates.cuic-devops-registry.cert ${SSL_CERT_FILE} + poetry config certificates.cuic-devops-publish.cert ${SSL_CERT_FILE} +fi \ No newline at end of file From 6b88680a581a6aad68ee144e8f72e0f3ebd634ad Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Mon, 28 Jul 2025 15:51:22 -0400 Subject: [PATCH 20/42] ci: fix major version error with cz bump --- .gitlab-ci.yml | 1 + pyproject.toml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4b4b110..13955d2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -26,6 +26,7 @@ build:build_poetry_containers: - echo "$DEV_SERVER_PASSWORD" | docker login $PROJECT_REGISTRY -u $DEV_SERVER_USERNAME --password-stdin - docker info script: + # Pull latest changes to debian base image - docker pull devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 - docker compose build opendis rules: diff --git a/pyproject.toml b/pyproject.toml index ad102df..95a9703 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,6 @@ docformatter = {extras = ["tomli"], version = "^1.7.5"} version = "1.1.0a" update_changelog_on_bump = false changelog_merge_prerelease = false -major_version_zero = true bump_message = "bump: $current_version → $new_version" version_files = [ "pyproject.toml:^version", From e7d16bbc9032aec36741df8a0749d293ffedc5d0 Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Mon, 28 Jul 2025 15:58:08 -0400 Subject: [PATCH 21/42] build: made version consistent with commitizen naming --- .env | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 0c5e42c..45b551c 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -version=1.1.0a \ No newline at end of file +version=1.1.0a0 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 95a9703..1bc1c48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "opendis" -version = "1.1.0a" +version = "1.1.0a0" description='implementation of DIS, IEEE-1278.1' authors = [ {name="Don McGregor", email="mcgredo@nps.edu"}, @@ -33,7 +33,7 @@ docformatter = {extras = ["tomli"], version = "^1.7.5"} [tool.commitizen] -version = "1.1.0a" +version = "1.1.0a0" update_changelog_on_bump = false changelog_merge_prerelease = false bump_message = "bump: $current_version → $new_version" From 7ee58130281f2d437740e798d1bdbc8b3e5a5cb1 Mon Sep 17 00:00:00 2001 From: Accel Gitlab Runner Date: Mon, 28 Jul 2025 19:59:23 +0000 Subject: [PATCH 22/42] =?UTF-8?q?bump:=201.1.0a0=20=E2=86=92=201.1.0a1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- CHANGELOG.md | 6 ++++++ pyproject.toml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.env b/.env index 45b551c..0ec5ca5 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -version=1.1.0a0 \ No newline at end of file +version=1.1.0a1 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e7ac9e1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +## 1.1.0a1 (2025-07-28) + +### Feat + +- alpha releases now indicate accel opendis releases +- allow a newer version of numpy diff --git a/pyproject.toml b/pyproject.toml index 1bc1c48..9bafe41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "opendis" -version = "1.1.0a0" +version = "1.1.0a1" description='implementation of DIS, IEEE-1278.1' authors = [ {name="Don McGregor", email="mcgredo@nps.edu"}, @@ -33,7 +33,7 @@ docformatter = {extras = ["tomli"], version = "^1.7.5"} [tool.commitizen] -version = "1.1.0a0" +version = "1.1.0a1" update_changelog_on_bump = false changelog_merge_prerelease = false bump_message = "bump: $current_version → $new_version" From 13b61d5cdd2a919f73f471f2667fbdf595f95eac Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Mon, 28 Jul 2025 16:01:21 -0400 Subject: [PATCH 23/42] docs: add more info to changelog about changes --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7ac9e1..49d01dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,5 +2,5 @@ ### Feat -- alpha releases now indicate accel opendis releases -- allow a newer version of numpy +- alpha releases indicate accel-opendis releases +- allow a newer version of numpy and support python >3.9 From f2e96b62753e019601735f24709f2d8a5fd17256 Mon Sep 17 00:00:00 2001 From: Accel Gitlab Runner Date: Mon, 28 Jul 2025 20:02:04 +0000 Subject: [PATCH 24/42] =?UTF-8?q?bump:=201.1.0a1=20=E2=86=92=201.1.0a2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- CHANGELOG.md | 2 ++ pyproject.toml | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 0ec5ca5..c024f44 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -version=1.1.0a1 \ No newline at end of file +version=1.1.0a2 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d01dd..8e89d1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.1.0a2 (2025-07-28) + ## 1.1.0a1 (2025-07-28) ### Feat diff --git a/pyproject.toml b/pyproject.toml index 9bafe41..4c913e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "opendis" -version = "1.1.0a1" +version = "1.1.0a2" description='implementation of DIS, IEEE-1278.1' authors = [ {name="Don McGregor", email="mcgredo@nps.edu"}, @@ -33,7 +33,7 @@ docformatter = {extras = ["tomli"], version = "^1.7.5"} [tool.commitizen] -version = "1.1.0a1" +version = "1.1.0a2" update_changelog_on_bump = false changelog_merge_prerelease = false bump_message = "bump: $current_version → $new_version" From 483cf2913449a5ad0188ba2c1c991f26df931f4b Mon Sep 17 00:00:00 2001 From: "Dr. Bryan T Weinstein" Date: Tue, 29 Jul 2025 08:11:27 -0400 Subject: [PATCH 25/42] test: adds pytest and coverage support --- .gitlab-ci.yml | 60 +++-- poetry.lock | 213 +++++++++++++++++- pyproject.toml | 2 + ...py => test_ElectromageneticEmissionPdu.py} | 0 ...tityStatePdu.py => test_EntityStatePdu.py} | 0 ...oordinates.py => test_RangeCoordinates.py} | 0 .../{testSetDataPdu.py => test_SetDataPdu.py} | 0 tests/{testSignalPdu.py => test_SignalPdu.py} | 0 ...ansmitterPdu.py => test_TransmitterPdu.py} | 0 9 files changed, 257 insertions(+), 18 deletions(-) rename tests/{testElectromageneticEmissionPdu.py => test_ElectromageneticEmissionPdu.py} (100%) rename tests/{testEntityStatePdu.py => test_EntityStatePdu.py} (100%) rename tests/{testRangeCoordinates.py => test_RangeCoordinates.py} (100%) rename tests/{testSetDataPdu.py => test_SetDataPdu.py} (100%) rename tests/{testSignalPdu.py => test_SignalPdu.py} (100%) rename tests/{testTransmitterPdu.py => test_TransmitterPdu.py} (100%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 13955d2..0910ca7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,41 +10,69 @@ variables: stages: - build + - test - version - publish + default: tags: - accel_docker_builder_runner -# -# Build -# -build:build_poetry_containers: - stage: build - before_script: - - echo "$DEV_SERVER_PASSWORD" | docker login $PROJECT_REGISTRY -u $DEV_SERVER_USERNAME --password-stdin - - docker info - script: - # Pull latest changes to debian base image - - docker pull devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 - - docker compose build opendis + +.base_job_rules: &base_job_rules rules: - if: $CI_COMMIT_TAG when: never - if: $CI_COMMIT_MESSAGE =~ /^bump:/ when: never - - if: $CI_PIPELINE_SOURCE == "trigger" - when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS when: never - if: $CI_ONLY_CHORES == "true" when: never - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $CI_RELEASE == "true" - when: on_success - if: $CI_COMMIT_BRANCH + +.base_image_job: &base_image_job + <<: *base_job_rules + image: + name: devops.cuic.mitre.org/accel-lab/opendis:ci_${CI_PIPELINE_ID} + entrypoint: [ "" ] + +# +# Build +# +build: + <<: *base_job_rules + stage: build + before_script: + - echo "$DEV_SERVER_PASSWORD" | docker login $PROJECT_REGISTRY -u $DEV_SERVER_USERNAME --password-stdin + script: + # Pull latest changes to debian base image + - docker pull devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 + - docker compose build opendis + + +# +# Test +# +test: + <<: *base_image_job + stage: test + script: + - echo "Running tests" + - pytest --junitxml=./test_results/test_results.xml --cov=opendis --cov-report term --cov-report=xml:./test_results/coverage.xml + after_script: + - echo 'All Done!' + coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/' + artifacts: + reports: + junit: test_results/test_results.xml + coverage_report: + coverage_format: cobertura + path: test_results/coverage.xml + # # Version # diff --git a/poetry.lock b/poetry.lock index f90c3b2..c2141f1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -257,6 +257,110 @@ termcolor = ">=1.1.0,<4.0.0" tomlkit = ">=0.5.3,<1.0.0" typing-extensions = {version = ">=4.0.1,<5.0.0", markers = "python_version < \"3.11\""} +[[package]] +name = "coverage" +version = "7.10.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "coverage-7.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1c86eb388bbd609d15560e7cc0eb936c102b6f43f31cf3e58b4fd9afe28e1372"}, + {file = "coverage-7.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b4ba0f488c1bdb6bd9ba81da50715a372119785458831c73428a8566253b86b"}, + {file = "coverage-7.10.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083442ecf97d434f0cb3b3e3676584443182653da08b42e965326ba12d6b5f2a"}, + {file = "coverage-7.10.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c1a40c486041006b135759f59189385da7c66d239bad897c994e18fd1d0c128f"}, + {file = "coverage-7.10.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3beb76e20b28046989300c4ea81bf690df84ee98ade4dc0bbbf774a28eb98440"}, + {file = "coverage-7.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc265a7945e8d08da28999ad02b544963f813a00f3ed0a7a0ce4165fd77629f8"}, + {file = "coverage-7.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:47c91f32ba4ac46f1e224a7ebf3f98b4b24335bad16137737fe71a5961a0665c"}, + {file = "coverage-7.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1a108dd78ed185020f66f131c60078f3fae3f61646c28c8bb4edd3fa121fc7fc"}, + {file = "coverage-7.10.1-cp310-cp310-win32.whl", hash = "sha256:7092cc82382e634075cc0255b0b69cb7cada7c1f249070ace6a95cb0f13548ef"}, + {file = "coverage-7.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:ac0c5bba938879c2fc0bc6c1b47311b5ad1212a9dcb8b40fe2c8110239b7faed"}, + {file = "coverage-7.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b45e2f9d5b0b5c1977cb4feb5f594be60eb121106f8900348e29331f553a726f"}, + {file = "coverage-7.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a7a4d74cb0f5e3334f9aa26af7016ddb94fb4bfa11b4a573d8e98ecba8c34f1"}, + {file = "coverage-7.10.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d4b0aab55ad60ead26159ff12b538c85fbab731a5e3411c642b46c3525863437"}, + {file = "coverage-7.10.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dcc93488c9ebd229be6ee1f0d9aad90da97b33ad7e2912f5495804d78a3cd6b7"}, + {file = "coverage-7.10.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa309df995d020f3438407081b51ff527171cca6772b33cf8f85344b8b4b8770"}, + {file = "coverage-7.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cfb8b9d8855c8608f9747602a48ab525b1d320ecf0113994f6df23160af68262"}, + {file = "coverage-7.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:320d86da829b012982b414c7cdda65f5d358d63f764e0e4e54b33097646f39a3"}, + {file = "coverage-7.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dc60ddd483c556590da1d9482a4518292eec36dd0e1e8496966759a1f282bcd0"}, + {file = "coverage-7.10.1-cp311-cp311-win32.whl", hash = "sha256:4fcfe294f95b44e4754da5b58be750396f2b1caca8f9a0e78588e3ef85f8b8be"}, + {file = "coverage-7.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:efa23166da3fe2915f8ab452dde40319ac84dc357f635737174a08dbd912980c"}, + {file = "coverage-7.10.1-cp311-cp311-win_arm64.whl", hash = "sha256:d12b15a8c3759e2bb580ffa423ae54be4f184cf23beffcbd641f4fe6e1584293"}, + {file = "coverage-7.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6b7dc7f0a75a7eaa4584e5843c873c561b12602439d2351ee28c7478186c4da4"}, + {file = "coverage-7.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:607f82389f0ecafc565813aa201a5cade04f897603750028dd660fb01797265e"}, + {file = "coverage-7.10.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f7da31a1ba31f1c1d4d5044b7c5813878adae1f3af8f4052d679cc493c7328f4"}, + {file = "coverage-7.10.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51fe93f3fe4f5d8483d51072fddc65e717a175490804e1942c975a68e04bf97a"}, + {file = "coverage-7.10.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e59d00830da411a1feef6ac828b90bbf74c9b6a8e87b8ca37964925bba76dbe"}, + {file = "coverage-7.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:924563481c27941229cb4e16eefacc35da28563e80791b3ddc5597b062a5c386"}, + {file = "coverage-7.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ca79146ee421b259f8131f153102220b84d1a5e6fb9c8aed13b3badfd1796de6"}, + {file = "coverage-7.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b225a06d227f23f386fdc0eab471506d9e644be699424814acc7d114595495f"}, + {file = "coverage-7.10.1-cp312-cp312-win32.whl", hash = "sha256:5ba9a8770effec5baaaab1567be916c87d8eea0c9ad11253722d86874d885eca"}, + {file = "coverage-7.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:9eb245a8d8dd0ad73b4062135a251ec55086fbc2c42e0eb9725a9b553fba18a3"}, + {file = "coverage-7.10.1-cp312-cp312-win_arm64.whl", hash = "sha256:7718060dd4434cc719803a5e526838a5d66e4efa5dc46d2b25c21965a9c6fcc4"}, + {file = "coverage-7.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ebb08d0867c5a25dffa4823377292a0ffd7aaafb218b5d4e2e106378b1061e39"}, + {file = "coverage-7.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f32a95a83c2e17422f67af922a89422cd24c6fa94041f083dd0bb4f6057d0bc7"}, + {file = "coverage-7.10.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c4c746d11c8aba4b9f58ca8bfc6fbfd0da4efe7960ae5540d1a1b13655ee8892"}, + {file = "coverage-7.10.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7f39edd52c23e5c7ed94e0e4bf088928029edf86ef10b95413e5ea670c5e92d7"}, + {file = "coverage-7.10.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab6e19b684981d0cd968906e293d5628e89faacb27977c92f3600b201926b994"}, + {file = "coverage-7.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5121d8cf0eacb16133501455d216bb5f99899ae2f52d394fe45d59229e6611d0"}, + {file = "coverage-7.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df1c742ca6f46a6f6cbcaef9ac694dc2cb1260d30a6a2f5c68c5f5bcfee1cfd7"}, + {file = "coverage-7.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:40f9a38676f9c073bf4b9194707aa1eb97dca0e22cc3766d83879d72500132c7"}, + {file = "coverage-7.10.1-cp313-cp313-win32.whl", hash = "sha256:2348631f049e884839553b9974f0821d39241c6ffb01a418efce434f7eba0fe7"}, + {file = "coverage-7.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:4072b31361b0d6d23f750c524f694e1a417c1220a30d3ef02741eed28520c48e"}, + {file = "coverage-7.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:3e31dfb8271937cab9425f19259b1b1d1f556790e98eb266009e7a61d337b6d4"}, + {file = "coverage-7.10.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1c4f679c6b573a5257af6012f167a45be4c749c9925fd44d5178fd641ad8bf72"}, + {file = "coverage-7.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:871ebe8143da284bd77b84a9136200bd638be253618765d21a1fce71006d94af"}, + {file = "coverage-7.10.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:998c4751dabf7d29b30594af416e4bf5091f11f92a8d88eb1512c7ba136d1ed7"}, + {file = "coverage-7.10.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:780f750a25e7749d0af6b3631759c2c14f45de209f3faaa2398312d1c7a22759"}, + {file = "coverage-7.10.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:590bdba9445df4763bdbebc928d8182f094c1f3947a8dc0fc82ef014dbdd8324"}, + {file = "coverage-7.10.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b2df80cb6a2af86d300e70acb82e9b79dab2c1e6971e44b78dbfc1a1e736b53"}, + {file = "coverage-7.10.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d6a558c2725bfb6337bf57c1cd366c13798bfd3bfc9e3dd1f4a6f6fc95a4605f"}, + {file = "coverage-7.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e6150d167f32f2a54690e572e0a4c90296fb000a18e9b26ab81a6489e24e78dd"}, + {file = "coverage-7.10.1-cp313-cp313t-win32.whl", hash = "sha256:d946a0c067aa88be4a593aad1236493313bafaa27e2a2080bfe88db827972f3c"}, + {file = "coverage-7.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e37c72eaccdd5ed1130c67a92ad38f5b2af66eeff7b0abe29534225db2ef7b18"}, + {file = "coverage-7.10.1-cp313-cp313t-win_arm64.whl", hash = "sha256:89ec0ffc215c590c732918c95cd02b55c7d0f569d76b90bb1a5e78aa340618e4"}, + {file = "coverage-7.10.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:166d89c57e877e93d8827dac32cedae6b0277ca684c6511497311249f35a280c"}, + {file = "coverage-7.10.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bed4a2341b33cd1a7d9ffc47df4a78ee61d3416d43b4adc9e18b7d266650b83e"}, + {file = "coverage-7.10.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ddca1e4f5f4c67980533df01430184c19b5359900e080248bbf4ed6789584d8b"}, + {file = "coverage-7.10.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:37b69226001d8b7de7126cad7366b0778d36777e4d788c66991455ba817c5b41"}, + {file = "coverage-7.10.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2f22102197bcb1722691296f9e589f02b616f874e54a209284dd7b9294b0b7f"}, + {file = "coverage-7.10.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1e0c768b0f9ac5839dac5cf88992a4bb459e488ee8a1f8489af4cb33b1af00f1"}, + {file = "coverage-7.10.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:991196702d5e0b120a8fef2664e1b9c333a81d36d5f6bcf6b225c0cf8b0451a2"}, + {file = "coverage-7.10.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae8e59e5f4fd85d6ad34c2bb9d74037b5b11be072b8b7e9986beb11f957573d4"}, + {file = "coverage-7.10.1-cp314-cp314-win32.whl", hash = "sha256:042125c89cf74a074984002e165d61fe0e31c7bd40ebb4bbebf07939b5924613"}, + {file = "coverage-7.10.1-cp314-cp314-win_amd64.whl", hash = "sha256:a22c3bfe09f7a530e2c94c87ff7af867259c91bef87ed2089cd69b783af7b84e"}, + {file = "coverage-7.10.1-cp314-cp314-win_arm64.whl", hash = "sha256:ee6be07af68d9c4fca4027c70cea0c31a0f1bc9cb464ff3c84a1f916bf82e652"}, + {file = "coverage-7.10.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d24fb3c0c8ff0d517c5ca5de7cf3994a4cd559cde0315201511dbfa7ab528894"}, + {file = "coverage-7.10.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1217a54cfd79be20512a67ca81c7da3f2163f51bbfd188aab91054df012154f5"}, + {file = "coverage-7.10.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:51f30da7a52c009667e02f125737229d7d8044ad84b79db454308033a7808ab2"}, + {file = "coverage-7.10.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ed3718c757c82d920f1c94089066225ca2ad7f00bb904cb72b1c39ebdd906ccb"}, + {file = "coverage-7.10.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc452481e124a819ced0c25412ea2e144269ef2f2534b862d9f6a9dae4bda17b"}, + {file = "coverage-7.10.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9d6f494c307e5cb9b1e052ec1a471060f1dea092c8116e642e7a23e79d9388ea"}, + {file = "coverage-7.10.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fc0e46d86905ddd16b85991f1f4919028092b4e511689bbdaff0876bd8aab3dd"}, + {file = "coverage-7.10.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80b9ccd82e30038b61fc9a692a8dc4801504689651b281ed9109f10cc9fe8b4d"}, + {file = "coverage-7.10.1-cp314-cp314t-win32.whl", hash = "sha256:e58991a2b213417285ec866d3cd32db17a6a88061a985dbb7e8e8f13af429c47"}, + {file = "coverage-7.10.1-cp314-cp314t-win_amd64.whl", hash = "sha256:e88dd71e4ecbc49d9d57d064117462c43f40a21a1383507811cf834a4a620651"}, + {file = "coverage-7.10.1-cp314-cp314t-win_arm64.whl", hash = "sha256:1aadfb06a30c62c2eb82322171fe1f7c288c80ca4156d46af0ca039052814bab"}, + {file = "coverage-7.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:57b6e8789cbefdef0667e4a94f8ffa40f9402cee5fc3b8e4274c894737890145"}, + {file = "coverage-7.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:85b22a9cce00cb03156334da67eb86e29f22b5e93876d0dd6a98646bb8a74e53"}, + {file = "coverage-7.10.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:97b6983a2f9c76d345ca395e843a049390b39652984e4a3b45b2442fa733992d"}, + {file = "coverage-7.10.1-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ddf2a63b91399a1c2f88f40bc1705d5a7777e31c7e9eb27c602280f477b582ba"}, + {file = "coverage-7.10.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47ab6dbbc31a14c5486420c2c1077fcae692097f673cf5be9ddbec8cdaa4cdbc"}, + {file = "coverage-7.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:21eb7d8b45d3700e7c2936a736f732794c47615a20f739f4133d5230a6512a88"}, + {file = "coverage-7.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:283005bb4d98ae33e45f2861cd2cde6a21878661c9ad49697f6951b358a0379b"}, + {file = "coverage-7.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:fefe31d61d02a8b2c419700b1fade9784a43d726de26495f243b663cd9fe1513"}, + {file = "coverage-7.10.1-cp39-cp39-win32.whl", hash = "sha256:e8ab8e4c7ec7f8a55ac05b5b715a051d74eac62511c6d96d5bb79aaafa3b04cf"}, + {file = "coverage-7.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:c36baa0ecde742784aa76c2b816466d3ea888d5297fda0edbac1bf48fa94688a"}, + {file = "coverage-7.10.1-py3-none-any.whl", hash = "sha256:fa2a258aa6bf188eb9a8948f7102a83da7c430a0dce918dbd8b60ef8fcb772d7"}, + {file = "coverage-7.10.1.tar.gz", hash = "sha256:ae2b4856f29ddfe827106794f3589949a57da6f0d38ab01e24ec35107979ba57"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + [[package]] name = "decli" version = "0.6.3" @@ -289,6 +393,25 @@ untokenize = ">=0.1.1,<0.2.0" [package.extras] tomli = ["tomli (>=2.0.0,<3.0.0) ; python_version < \"3.11\""] +[[package]] +name = "exceptiongroup" +version = "1.3.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "flake8" version = "7.3.0" @@ -422,6 +545,18 @@ perf = ["ipython"] test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + [[package]] name = "isort" version = "6.0.1" @@ -638,6 +773,22 @@ docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-a test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] type = ["mypy (>=1.14.1)"] +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "prompt-toolkit" version = "3.0.51" @@ -695,6 +846,64 @@ files = [ {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, ] +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.4.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + [[package]] name = "pyyaml" version = "6.0.2" @@ -807,7 +1016,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_full_version <= \"3.11.0a6\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -914,4 +1123,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "c1d1f5d82e30c2b254ed4667ceb803b380ccd93089edcdb15c58a29ec8eeb1b5" +content-hash = "2d7d98e29fbd34f801edf8190bf9e7f2e98eaa61d86d80c558ca51b0eac9c410" diff --git a/pyproject.toml b/pyproject.toml index 4c913e4..0340fe2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,8 @@ flake8-print = "^5.0.0" flake8-docstrings = "^1.7.0" flake8-docstrings-complete = "^1.3.0" docformatter = {extras = ["tomli"], version = "^1.7.5"} +pytest = "^8" +pytest-cov = "^4" [tool.commitizen] diff --git a/tests/testElectromageneticEmissionPdu.py b/tests/test_ElectromageneticEmissionPdu.py similarity index 100% rename from tests/testElectromageneticEmissionPdu.py rename to tests/test_ElectromageneticEmissionPdu.py diff --git a/tests/testEntityStatePdu.py b/tests/test_EntityStatePdu.py similarity index 100% rename from tests/testEntityStatePdu.py rename to tests/test_EntityStatePdu.py diff --git a/tests/testRangeCoordinates.py b/tests/test_RangeCoordinates.py similarity index 100% rename from tests/testRangeCoordinates.py rename to tests/test_RangeCoordinates.py diff --git a/tests/testSetDataPdu.py b/tests/test_SetDataPdu.py similarity index 100% rename from tests/testSetDataPdu.py rename to tests/test_SetDataPdu.py diff --git a/tests/testSignalPdu.py b/tests/test_SignalPdu.py similarity index 100% rename from tests/testSignalPdu.py rename to tests/test_SignalPdu.py diff --git a/tests/testTransmitterPdu.py b/tests/test_TransmitterPdu.py similarity index 100% rename from tests/testTransmitterPdu.py rename to tests/test_TransmitterPdu.py From 041a4f9af113241e81d96cfad448385b037db4d5 Mon Sep 17 00:00:00 2001 From: Accel Gitlab Runner Date: Tue, 29 Jul 2025 12:12:26 +0000 Subject: [PATCH 26/42] =?UTF-8?q?bump:=201.1.0a2=20=E2=86=92=201.1.0a3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- CHANGELOG.md | 2 ++ pyproject.toml | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.env b/.env index c024f44..d54fdaf 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -version=1.1.0a2 \ No newline at end of file +version=1.1.0a3 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e89d1c..efbdebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.1.0a3 (2025-07-29) + ## 1.1.0a2 (2025-07-28) ## 1.1.0a1 (2025-07-28) diff --git a/pyproject.toml b/pyproject.toml index 0340fe2..4a17e9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "opendis" -version = "1.1.0a2" +version = "1.1.0a3" description='implementation of DIS, IEEE-1278.1' authors = [ {name="Don McGregor", email="mcgredo@nps.edu"}, @@ -35,7 +35,7 @@ pytest-cov = "^4" [tool.commitizen] -version = "1.1.0a2" +version = "1.1.0a3" update_changelog_on_bump = false changelog_merge_prerelease = false bump_message = "bump: $current_version → $new_version" From 402d949cfe7b45dbc1cf76b1fb9a324375dc6a65 Mon Sep 17 00:00:00 2001 From: "Dr. Bryan T Weinstein" Date: Tue, 29 Jul 2025 15:12:28 -0400 Subject: [PATCH 27/42] fix: fixes broken examples (dis_receiver and dis_sender), corrects function arg types to `DataOutputStream`, fixes error where pduType and protocolFamily were always set to 0 for all message types, and made struct8->32 have the correct type --- examples/dis_receiver.py | 12 +++++--- examples/dis_sender.py | 7 +++-- opendis/DataOutputStream.py | 5 ++-- opendis/__init__.py | 5 ++++ opendis/dis7.py | 57 ++++++++++++++++++++++--------------- opendis/types.py | 6 ++-- tests/test_examples.py | 45 +++++++++++++++++++++++++++++ 7 files changed, 102 insertions(+), 35 deletions(-) create mode 100644 tests/test_examples.py diff --git a/examples/dis_receiver.py b/examples/dis_receiver.py index dbea3a7..9f17705 100644 --- a/examples/dis_receiver.py +++ b/examples/dis_receiver.py @@ -23,8 +23,11 @@ gps = GPS(); def recv(): + print('Reading from socket...') + data = udpSocket.recv(1024) # buffer size in bytes - pdu = createPdu(data); + pdu = createPdu(data) + pduTypeName = pdu.__class__.__name__ if pdu.pduType == 1: # PduTypeDecoders.EntityStatePdu: @@ -50,6 +53,7 @@ def recv(): else: print("Received {}, {} bytes".format(pduTypeName, len(data)), flush=True) - -while True: - recv() +if __name__ == "__main__": + while True: + recv() + time.sleep(.5) \ No newline at end of file diff --git a/examples/dis_sender.py b/examples/dis_sender.py index e9b2cf7..c7a0734 100644 --- a/examples/dis_sender.py +++ b/examples/dis_sender.py @@ -22,6 +22,7 @@ def send(): pdu = EntityStatePdu() + pdu.entityID.entityID = 42 pdu.entityID.siteID = 17 pdu.entityID.applicationID = 23 @@ -43,6 +44,7 @@ def send(): pdu.entityOrientation.theta = montereyLocation[4] pdu.entityOrientation.phi = montereyLocation[5] + print("Entity state pdu type is ", pdu.pduType) memoryStream = BytesIO() outputStream = DataOutputStream(memoryStream) @@ -52,6 +54,7 @@ def send(): while True: udpSocket.sendto(data, (DESTINATION_ADDRESS, UDP_PORT)) print("Sent {}. {} bytes".format(pdu.__class__.__name__, len(data))) - time.sleep(60) + time.sleep(1) -send() +if __name__ == "__main__": + send() diff --git a/opendis/DataOutputStream.py b/opendis/DataOutputStream.py index 95d2fc4..55fab2d 100644 --- a/opendis/DataOutputStream.py +++ b/opendis/DataOutputStream.py @@ -7,7 +7,6 @@ from io import BufferedIOBase import struct - class DataOutputStream: def __init__(self, stream: BufferedIOBase): self.stream = stream @@ -15,10 +14,10 @@ def __init__(self, stream: BufferedIOBase): def write_boolean(self, boolean: bool) -> None: self.stream.write(struct.pack('?', boolean)) - def write_byte(self, val: bytes) -> None: + def write_byte(self, val: int) -> None: self.stream.write(struct.pack('b', val)) - def write_unsigned_byte(self, val: bytes) -> None: + def write_unsigned_byte(self, val: int) -> None: self.stream.write(struct.pack('B', val)) def write_char(self, val: str) -> None: diff --git a/opendis/__init__.py b/opendis/__init__.py index baaa47f..7bdf293 100644 --- a/opendis/__init__.py +++ b/opendis/__init__.py @@ -1,2 +1,7 @@ # Marker for a python package directory name = "opendis" + +from pathlib import Path + +__root = Path(__file__).parent +root = str(__root) \ No newline at end of file diff --git a/opendis/dis7.py b/opendis/dis7.py index 4ca5472..1fa0736 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -1464,7 +1464,7 @@ def __init__( temperature: float32 = -273.15, # in degrees Celsius componentIdentification: enum8 = 0, # [UID 314] componentDamageStatus: enum8 = 0, # [UID 315] - componentVisualDamageStatus: struct8 = b'0', # [UID 317] + componentVisualDamageStatus: struct8 = 0, # [UID 317] componentVisualSmokeColor: enum8 = 0, # [UID 316] fireEventID: "EventIdentifier | None" = None): self.padding: uint16 = 0 @@ -1587,8 +1587,8 @@ class SecondaryOperationalData: """ def __init__(self, - operationalData1: struct8 = b'0', - operationalData2: struct8 = b'0', + operationalData1: struct8 = 0, + operationalData2: struct8 = 0, numberOfIFFFundamentalParameterRecords: uint16 = 0): self.operationalData1 = operationalData1 """additional operational characteristics of the IFF emitting system. Each 8-bit field will vary depending on the system type.""" @@ -3861,7 +3861,7 @@ def __init__(self, protocolFamily: enum8 = 0, # [UID 5] timestamp: uint32 = 0, # (See 6.2.88) pduLength: uint16 = 0, # in bytes - pduStatus: struct8 = b'0'): # (See 6.2.67) + pduStatus: struct8 = 0): # (See 6.2.67) self.protocolVersion = protocolVersion """The version of the protocol. 5=DIS-1995, 6=DIS-1998, 7=DIS-2009.""" self.exerciseID = exerciseID @@ -3911,20 +3911,31 @@ class PduSuperclass: def __init__(self, protocolVersion: enum8 = 7, # [UID 3] exerciseID=0, # Exercise ID (6.2.34) - pduType: enum8 = 0, # [UID 4] - protocolFamily: enum8 = 0, # [UID 5] + pduType: enum8 = None, # [UID 4], set on subclasses + protocolFamily: enum8 = None, # [UID 5], set on subclasses timestamp: uint32 = 0, # (See 6.2.88) pduLength: uint16 = 0): # in bytes self.protocolVersion = protocolVersion """The version of the protocol. 5=DIS-1995, 6=DIS-1998, 7=DIS-2009.""" + self.exerciseID = exerciseID """Exercise ID""" - self.pduType = pduType + + if pduType is None and not hasattr(self, 'pduType'): + raise ValueError("pduType must be set on subclasses of PduSuperclass or passed in via init") + elif pduType is not None: + self.pduType = pduType """Type of pdu, unique for each PDU class""" - self.protocolFamily = protocolFamily + + if protocolFamily is None and not hasattr(self, 'pduType'): + raise ValueError("protocolFamily must be set on subclasses of PduSuperclass or passed in via init") + elif pduType is not None: + self.protocolFamily = protocolFamily """value that refers to the protocol family, eg SimulationManagement, et""" + self.timestamp = timestamp """Timestamp value""" + self.length = pduLength """Length, in bytes, of the PDU""" @@ -4124,7 +4135,7 @@ class PduStatus: a byte. """ - def __init__(self, pduStatus: struct8 = b'0'): + def __init__(self, pduStatus: struct8 = 0): self.pduStatus = pduStatus """Bit fields. The semantics of the bit fields depend on the PDU type""" @@ -4169,7 +4180,7 @@ class Pdu(PduSuperclass): def __init__(self, pduStatus: "PduStatus | None" = None): # (See 6.2.67) super(Pdu, self).__init__() - self.pduStatus = pduStatus or PduStatus() + self.pduStatus = pduStatus or PduStatus().pduStatus """PDU Status Record. Described in 6.2.67. This field is not present in earlier DIS versions""" self.padding: uint8 = 0 """zero-filled array of padding""" @@ -4239,7 +4250,7 @@ def __init__(self, entityLinearVelocity: "Vector3Float | None" = None, entityLocation: "Vector3Double | None" = None, entityOrientation: "EulerAngles | None" = None, - entityAppearance: struct32 = b'0000', # [UID 31-43] + entityAppearance: struct32 = 0, # [UID 31-43] variableParameters: list["VariableParameter"] | None = None): super(EntityStateUpdatePdu, self).__init__() self.entityID = entityID or EntityID() @@ -4696,7 +4707,7 @@ class IntercomSignalPdu(RadioCommunicationsFamilyPdu): def __init__(self, entityID: "EntityID | ObjectID | UnattachedIdentifier | None" = None, communicationsDeviceID: uint16 = 0, - encodingScheme: struct16 = b'00', + encodingScheme: struct16 = 00, tdlType: uint16 = 0, # [UID 178] sampleRate: uint32 = 0, samples: uint16 = 0, @@ -5279,10 +5290,10 @@ def __init__(self, entityLinearVelocity: "Vector3Float | None" = None, entityLocation: "Vector3Double | None" = None, entityOrientation: "EulerAngles | None" = None, - entityAppearance: struct32 = b'0000', # [UID 31-43] + entityAppearance: struct32 = 0, # [UID 31-43] deadReckoningParameters: "DeadReckoningParameters | None" = None, marking: "EntityMarking | None" = None, - capabilities: struct32 = b'0000', # [UID 55] + capabilities: struct32 = 0, # [UID 55] variableParameters: list["VariableParameter"] | None = None): super(EntityStatePdu, self).__init__() self.entityID = entityID or EntityID() @@ -5434,7 +5445,7 @@ def __init__(self, power: float32 = 0.0, # in decibel-milliwatts modulationType: "ModulationType | None" = None, cryptoSystem: enum16 = 0, # [UID 166] - cryptoKeyId: struct16 = b'00', # See Table 175 + cryptoKeyId: struct16 = 0, # See Table 175 modulationParameterCount: uint8 = 0, # in bytes modulationParametersList=None, antennaPatternList=None): @@ -5955,7 +5966,7 @@ def __init__(self, objectType: "ObjectType | None" = None, objectLocation: "Vector3Double | None" = None, objectOrientation: "EulerAngles | None" = None, - objectAppearance: struct32 | struct16 = b'0000', # [UID 229] + objectAppearance: struct32 | struct16 = 0, # [UID 229] requesterID: "SimulationAddress | None" = None, receivingID: "SimulationAddress | None" = None): super(PointObjectStatePdu, self).__init__() @@ -6398,8 +6409,8 @@ def __init__(self, forceId: enum8 = 0, # [UID 6] modifications: enum8 = 0, # [UID 242] objectType: "ObjectType | None" = None, - specificObjectAppearance: struct32 = b'0000', - generalObjectAppearance: struct16 = b'00', # [UID 229] + specificObjectAppearance: struct32 = 0, + generalObjectAppearance: struct16 = 0, # [UID 229] requesterID: "SimulationAddress | None" = None, receivingID: "SimulationAddress | None" = None, objectLocation: list["Vector3Double"] | None = None): @@ -6769,7 +6780,7 @@ def __init__(self, peakIrradiance=0.0, pulseRepetitionFrequency: float32 = 0.0, # in Hz pulseWidth: float32 = 0, # in seconds - flags: struct16 = b'00', # [UID 313] + flags: struct16 = 0, # [UID 313] pulseShape: enum8 = 0, # [UID 312] dERecords: list | None = None): super(DirectedEnergyFirePdu, self).__init__() @@ -7240,7 +7251,7 @@ def __init__(self, eventID: "EventIdentifier | None" = None, stateChangeIndicator: enum8 = 0, # [UID 143] passiveParameterIndex: enum16 = 0, # [UID 148] - propulsionPlantConfiguration: struct8 = b'0', # [UID 149] + propulsionPlantConfiguration: struct8 = 0, # [UID 149] shaftRPMs: list | None = None, # positive = clockwise apaData: list | None = None, emitterSystems: list | None = None): @@ -7333,7 +7344,7 @@ class IntercomControlPdu(RadioCommunicationsFamilyPdu): def __init__(self, controlType: enum8 = 0, # [UID 180] - communicationsChannelType: struct8 = b'0', # [UID 416], [UID 181] + communicationsChannelType: struct8 = 0, # [UID 416], [UID 181] sourceEntityID: "EntityID | UnattachedIdentifier | None" = None, sourceCommunicationsDeviceID: uint16 = 0, sourceLineID: uint8 = 0, @@ -7417,7 +7428,7 @@ class SignalPdu(RadioCommunicationsFamilyPdu): def __init__(self, entityID: "EntityID | ObjectIdentifier | UnattachedIdentifier | None" = None, radioID: uint16 = 0, - encodingScheme: struct16 = b'00', # (Table 177), [UID 271], [UID 270] + encodingScheme: struct16 = 0, # (Table 177), [UID 271], [UID 270] tdlType: enum16 = 0, # [UID 178] sampleRate: uint32 = 0, samples: uint16 = 0, @@ -7614,7 +7625,7 @@ class StopFreezeReliablePdu(SimulationManagementWithReliabilityFamilyPdu): def __init__(self, realWorldTime: "ClockTime | None" = None, reason: enum8 = 0, # [UID 67] - frozenBehavior: struct8 = b'0', # [UID 68] + frozenBehavior: struct8 = 0, # [UID 68] requiredReliabilityService: enum8 = 0, # [UID 74] requestID: uint32 = 0): super(StopFreezeReliablePdu, self).__init__() diff --git a/opendis/types.py b/opendis/types.py index 9b5b4f9..38de5ff 100644 --- a/opendis/types.py +++ b/opendis/types.py @@ -22,9 +22,9 @@ uint64 = int float32 = float float64 = float -struct8 = bytes -struct16 = bytes -struct32 = bytes +struct8 = int +struct16 = int +struct32 = int char8 = str char16 = str diff --git a/tests/test_examples.py b/tests/test_examples.py new file mode 100644 index 0000000..f4a1d85 --- /dev/null +++ b/tests/test_examples.py @@ -0,0 +1,45 @@ + +import subprocess +from opendis import root +import os +import time +import sys +from pathlib import Path + +def test_dis_sender_and_receiver(): + os.chdir(Path(root).parent) + + # Need -u so the stdout is immediately sent to pipe + receive_proc = subprocess.Popen(['python', '-u', 'examples/dis_receiver.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + send_proc = subprocess.Popen(['python', '-u', 'examples/dis_sender.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Run for about 3 seconds + for i in range(3): + print(f"Iteration {i+1}: Waiting for processes to complete...") + + if receive_proc.poll() is not None: + if receive_proc.returncode != 0: + print('Receive failure! ', receive_proc.returncode) + stdout, stderr = receive_proc.communicate() + raise ValueError(f"Receive process failed. \n STDOUT: {stdout} \n STDERR: {stderr}") + if send_proc.poll() is not None: + if send_proc.returncode != 0: + print('Send failure!', send_proc.returncode) + stdout, stderr = send_proc.communicate() + raise ValueError(f"Send process failed. \n STDOUT: {stdout} \n STDERR: {stderr}") + + time.sleep(1) + + receive_proc.terminate() + send_proc.terminate() + + receive_stdout, _ = receive_proc.communicate() + print('Reciver stdout is:', receive_stdout) + + send_stdout, _ = send_proc.communicate() + print('Sender stdout is:', send_stdout) + + assert receive_stdout.find("Received EntityStatePdu") != -1, "Did not receive expected PDU type in receiver output" + +if __name__ == "__main__": + test_dis_sender_and_receiver() From a63d021188ea4e56f4101c2cc3eeca57d04b28e7 Mon Sep 17 00:00:00 2001 From: Accel Gitlab Runner Date: Tue, 29 Jul 2025 19:13:25 +0000 Subject: [PATCH 28/42] =?UTF-8?q?bump:=201.1.0a3=20=E2=86=92=201.1.0a4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- CHANGELOG.md | 6 ++++++ pyproject.toml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.env b/.env index d54fdaf..1fe6e50 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -version=1.1.0a3 \ No newline at end of file +version=1.1.0a4 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index efbdebe..956fb6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.1.0a4 (2025-07-29) + +### Fix + +- fixes broken examples (dis_receiver and dis_sender), corrects function arg types to `DataOutputStream`, fixes error where pduType and protocolFamily were always set to 0 for all message types, and made struct8->32 have the correct type + ## 1.1.0a3 (2025-07-29) ## 1.1.0a2 (2025-07-28) diff --git a/pyproject.toml b/pyproject.toml index 4a17e9b..083b46b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "opendis" -version = "1.1.0a3" +version = "1.1.0a4" description='implementation of DIS, IEEE-1278.1' authors = [ {name="Don McGregor", email="mcgredo@nps.edu"}, @@ -35,7 +35,7 @@ pytest-cov = "^4" [tool.commitizen] -version = "1.1.0a3" +version = "1.1.0a4" update_changelog_on_bump = false changelog_merge_prerelease = false bump_message = "bump: $current_version → $new_version" From 15bac95932097acfe2aff92fc0ad9d995cd3b01e Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Wed, 30 Jul 2025 08:16:23 -0400 Subject: [PATCH 29/42] ci: add github ci and remove gitlab ci (#2) --- .github/workflows/build_test.yml | 43 ++++++++++++ .gitlab-ci.yml | 114 ------------------------------- ci/auto-bump.sh | 22 ------ ci/compose.override.yml | 3 - ci/docker-compose.yml | 8 --- docker/Dockerfile | 32 --------- poetry.lock | 56 ++------------- pyproject.toml | 2 +- 8 files changed, 49 insertions(+), 231 deletions(-) create mode 100644 .github/workflows/build_test.yml delete mode 100644 .gitlab-ci.yml delete mode 100755 ci/auto-bump.sh delete mode 100644 ci/compose.override.yml delete mode 100644 ci/docker-compose.yml delete mode 100644 docker/Dockerfile diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml new file mode 100644 index 0000000..0b549ca --- /dev/null +++ b/.github/workflows/build_test.yml @@ -0,0 +1,43 @@ +# This workflow will install opendis dependencies, and then run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Build and Test OpenDis + +on: + push: + branches: [ "master" ] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + pip install poetry + poetry install --with dev + - name: Test with pytest + run: | + poetry run pytest --junitxml=./test_results/test_results.xml --cov=opendis --cov-report term --cov-report=xml:./test_results/coverage.xml + - name: Upload Test Results + uses: dorny/test-reporter@v1 + if: always() + with: + name: JUnit Tests + path: test_results/test_results.xml + reporter: java-junit + - name: Pytest coverage comment + uses: MishaKav/pytest-coverage-comment@main + with: + pytest-xml-coverage-path: ./test_results/coverage.xml + junitxml-path: ./test_results/test_results.xml \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 0910ca7..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,114 +0,0 @@ -variables: - COMPOSE_FILE: "ci/docker-compose.yml" - PROJECT_REGISTRY: ${PROJECT_REGISTRY} - NAMESPACE: ${NAMESPACE} - APP_NAME: "accel-api" - GIT_USER: "Accel Gitlab Runner" - GIT_EMAIL: "runner@accel.mitre.org" - CURRENT_USER: "None" - GIT_STRATEGY: clone - -stages: - - build - - test - - version - - publish - - -default: - tags: - - accel_docker_builder_runner - - -.base_job_rules: &base_job_rules - rules: - - if: $CI_COMMIT_TAG - when: never - - if: $CI_COMMIT_MESSAGE =~ /^bump:/ - when: never - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS - when: never - - if: $CI_ONLY_CHORES == "true" - when: never - - if: $CI_COMMIT_BRANCH - - -.base_image_job: &base_image_job - <<: *base_job_rules - image: - name: devops.cuic.mitre.org/accel-lab/opendis:ci_${CI_PIPELINE_ID} - entrypoint: [ "" ] - -# -# Build -# -build: - <<: *base_job_rules - stage: build - before_script: - - echo "$DEV_SERVER_PASSWORD" | docker login $PROJECT_REGISTRY -u $DEV_SERVER_USERNAME --password-stdin - script: - # Pull latest changes to debian base image - - docker pull devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 - - docker compose build opendis - - -# -# Test -# -test: - <<: *base_image_job - stage: test - script: - - echo "Running tests" - - pytest --junitxml=./test_results/test_results.xml --cov=opendis --cov-report term --cov-report=xml:./test_results/coverage.xml - after_script: - - echo 'All Done!' - coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/' - artifacts: - reports: - junit: test_results/test_results.xml - coverage_report: - coverage_format: cobertura - path: test_results/coverage.xml - -# -# Version -# -auto-bump-dev: - stage: version - image: - name: devops.cuic.mitre.org/accel-lab/opendis:ci_${CI_PIPELINE_ID} - script: - - ./ci/auto-bump.sh - rules: - - if: $CI_COMMIT_TAG - when: never - - if: $CI_ONLY_CHORES == "true" - when: never - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE =~ /^bump:/ - when: never - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - when: on_success - - - -# -# Publish -# -include: - # Test Publish Poetry Component - - component: $CI_SERVER_FQDN/cuic-lab/cuic-devops/cicd-templates/poetry-project-ci/publish-poetry@main - inputs: - stage: publish - runner_tag: accel_docker_builder_runner - image: devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 - - -publish:poetry_build_and_publish: - # Override the build rules for the include - rules: - - if: $CI_COMMIT_TAG # publish on tag - when: on_success - - when: never diff --git a/ci/auto-bump.sh b/ci/auto-bump.sh deleted file mode 100755 index d42640a..0000000 --- a/ci/auto-bump.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -set -o errexit - -git config --global user.name "$GIT_USER" -git config --global user.email "$GIT_EMAIL" -git config http.https://gitlab.mitre.org.sslcainfo /etc/ssl/certs/ca-certificates.crt -git remote set-url origin \ - "https://gitlab-ci-token:${GITLAB_CI_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" - -echo $(git remote get-url origin) - -exists=`git show-ref refs/heads/main` && if [ -n "$exists" ]; then git branch -D main; fi -git checkout -b main - -git status - -echo "Auto Bumping Repo" -cz bump --changelog --check-consistency --yes --no-verify --prerelease alpha - -git status -git push origin main:$CI_COMMIT_REF_SLUG --tag -f diff --git a/ci/compose.override.yml b/ci/compose.override.yml deleted file mode 100644 index dd6e988..0000000 --- a/ci/compose.override.yml +++ /dev/null @@ -1,3 +0,0 @@ -services: - opendis: - image: devops.cuic.mitre.org/accel-lab/opendis:ci_${CI_PIPELINE_ID} \ No newline at end of file diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml deleted file mode 100644 index 02da167..0000000 --- a/ci/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: opendis-ci - -include: - - path: - - ../docker-compose.yml - - compose.override.yml - env_file: - - ../.env \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 30f6d1a..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM devops.cuic.mitre.org/cuic-lab/mitre-cuic-debian-bookworm:py3.11 - -ENV HTTP_PROXY=devops.cuic.mitre.org -ENV APP_HOME=/home/cuic-dev/accel-open-dis-python -ENV PROJECT_REGISTRY=devops.cuic.mitre.org - -WORKDIR $APP_HOME - -## Copy code into container -COPY . . - -RUN poetry config certificates.cuic-devops-registry.cert /etc/ssl/certs/ca-certificates.crt - -RUN echo `poetry --version` - -# Use the below to use secrets -RUN --mount=type=secret,id=dev_server_password \ - --mount=type=secret,id=dev_server_username \ - DEV_SERVER_USERNAME=$(cat /run/secrets/dev_server_username) \ - && export DEV_SERVER_USERNAME \ - && DEV_SERVER_PASSWORD=$(cat /run/secrets/dev_server_password) \ - && export DEV_SERVER_PASSWORD \ - && poetry config http-basic.cuic-devops-registry $DEV_SERVER_USERNAME $DEV_SERVER_PASSWORD \ - && poetry install --no-root --only main,dev - -ENTRYPOINT [ "" ] - -# Copy code into correct location into site-packages since poetry does not cooperate, even without no-root -WORKDIR /usr/local/lib/python${PYTHON_VERSION}/site-packages/opendis -COPY ./opendis . - -WORKDIR $APP_HOME diff --git a/poetry.lock b/poetry.lock index c2141f1..5465de1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -184,22 +184,6 @@ files = [ {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version == \"3.9\"" -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "click" version = "8.2.1" @@ -207,7 +191,6 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["dev"] -markers = "python_version != \"3.9\"" files = [ {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, @@ -245,10 +228,7 @@ argcomplete = ">=1.12.1,<3.7" charset-normalizer = ">=2.1.0,<4" colorama = ">=0.4.1,<1.0" decli = ">=0.6.0,<1.0" -importlib-metadata = [ - {version = ">=8.0.0,<8.7.0 || >8.7.0,<9.0.0", markers = "python_version == \"3.9\""}, - {version = ">=8.0.0,<9.0.0", markers = "python_version != \"3.9\""}, -] +importlib-metadata = {version = ">=8.0.0,<9.0.0", markers = "python_version != \"3.9\""} jinja2 = ">=2.10.3" packaging = ">=19" pyyaml = ">=3.08" @@ -400,7 +380,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, @@ -495,31 +475,6 @@ files = [ flake8 = ">=3.0" pycodestyle = "*" -[[package]] -name = "importlib-metadata" -version = "8.6.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version == \"3.9\"" -files = [ - {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, - {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] - [[package]] name = "importlib-metadata" version = "8.7.0" @@ -527,7 +482,6 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "python_version != \"3.9\"" files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, @@ -1071,7 +1025,7 @@ description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, @@ -1122,5 +1076,5 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" -python-versions = ">=3.9,<4.0" -content-hash = "2d7d98e29fbd34f801edf8190bf9e7f2e98eaa61d86d80c558ca51b0eac9c410" +python-versions = ">=3.10,<4.0" +content-hash = "a980c3ec9ee4d67ea4eb3246a24ab012d38cb1b49427371920d6fd6e829e21dd" diff --git a/pyproject.toml b/pyproject.toml index 083b46b..423161b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = [ {name="Don McGregor", email="mcgredo@nps.edu"}, ] readme = "README.md" -requires-python = ">=3.9,<4.0" +requires-python = ">=3.10,<4.0" repository = 'https://github.com/open-dis/open-dis-python' documentation = "https://open-dis.org/" classifiers=[ From 15c87e1b8d14ff50163f256db00676944454c261 Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Wed, 30 Jul 2025 08:23:38 -0400 Subject: [PATCH 30/42] chore: removed other unecessary gitlab files --- CHANGELOG.md | 16 ---------------- docker-compose.yml | 23 ----------------------- 2 files changed, 39 deletions(-) delete mode 100644 CHANGELOG.md delete mode 100644 docker-compose.yml diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 956fb6d..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,16 +0,0 @@ -## 1.1.0a4 (2025-07-29) - -### Fix - -- fixes broken examples (dis_receiver and dis_sender), corrects function arg types to `DataOutputStream`, fixes error where pduType and protocolFamily were always set to 0 for all message types, and made struct8->32 have the correct type - -## 1.1.0a3 (2025-07-29) - -## 1.1.0a2 (2025-07-28) - -## 1.1.0a1 (2025-07-28) - -### Feat - -- alpha releases indicate accel-opendis releases -- allow a newer version of numpy and support python >3.9 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 22077e3..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: opendis - -services: - opendis: - # By default just runs tests when spun up; see entrypoint - image: devops.cuic.mitre.org/accel-lab/opendis:${version?:err} - pull_policy: build - build: - context: . - dockerfile: ./docker/Dockerfile - secrets: - - dev_server_username - - dev_server_password - container_name: opendis - volumes: - - .:/home/cuic-dev/accel-open-dis-python - - ./opendis:/usr/local/lib/python3.11/site-packages/opendis - -secrets: - dev_server_password: - environment: DEV_SERVER_PASSWORD - dev_server_username: - environment: DEV_SERVER_USERNAME From c24e39a4b49e69735a5473d8c7d59d772c99ee53 Mon Sep 17 00:00:00 2001 From: Bryan Weinstein Date: Wed, 30 Jul 2025 08:31:06 -0400 Subject: [PATCH 31/42] chore: removed uneeded file --- setup | 52 ---------------------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 setup diff --git a/setup b/setup deleted file mode 100644 index e9564ba..0000000 --- a/setup +++ /dev/null @@ -1,52 +0,0 @@ -echo "SETTING CURRENT USER" -export CURRENT_USER=$(id -u):$(id -g) - -if [[ $SHELL == *"bash"* ]]; then - read -p $'Enter your CUIC Devops Server username: \n> ' DEV_SERVER_USERNAME - export DEV_SERVER_USERNAME - - read -sp $'Enter your CUIC Devops Server password: \n> ' DEV_SERVER_PASSWORD - export DEV_SERVER_PASSWORD -else - echo $'Enter your CUIC Devops Server username:' - read DEV_SERVER_USERNAME - export DEV_SERVER_USERNAME - - echo $'Enter your CUIC Devops Server password:' - stty -echo - read DEV_SERVER_PASSWORD - export DEV_SERVER_PASSWORD - stty echo -fi - -export DEV_NPM_TOKEN=`echo -n "${DEV_SERVER_USERNAME}:${DEV_SERVER_PASSWORD}" | openssl base64` - -export HTTP_PROXY=devops.cuic.mitre.org - -# Docker login -echo $DEV_SERVER_PASSWORD | docker login devops.cuic.mitre.org --username ${DEV_SERVER_USERNAME} --password-stdin - -# Poetry credentials -if ! command -v poetry &> /dev/null -then - echo "Poetry not installed. Skipping..." - return -fi -poetry config http-basic.cuic-devops-registry ${DEV_SERVER_USERNAME} ${DEV_SERVER_PASSWORD} -if [ -f "/etc/ssl/certs/ca-certificates.crt" ]; then - CERT_PATH="/etc/ssl/certs/ca-certificates.crt" -elif [[ "$OSTYPE" == "darwin"* ]]; then - OPENSSL_DIR=$(openssl version -d | cut -d'"' -f2) - CERT_PATH="${OPENSSL_DIR}/cert.pem" -fi -poetry config certificates.cuic-devops-registry.cert $CERT_PATH -poetry config repositories.cuic-devops-publish https://devops.cuic.mitre.org/nexus/repository/python-hosted/ -poetry config http-basic.cuic-devops-publish ${DEV_SERVER_USERNAME} ${DEV_SERVER_PASSWORD} - -poetry config certificates.cuic-devops-publish.cert $CERT_PATH -poetry config certificates.cuic-devops-publish.cert /etc/ssl/certs/ca-certificates.crt - -if [[ -n "${SSL_CERT_FILE}" ]]; then - poetry config certificates.cuic-devops-registry.cert ${SSL_CERT_FILE} - poetry config certificates.cuic-devops-publish.cert ${SSL_CERT_FILE} -fi \ No newline at end of file From 7174fdc823adf6de5290f07b70849436ab57056a Mon Sep 17 00:00:00 2001 From: Quinn Gavin Date: Tue, 26 Aug 2025 17:22:02 -0700 Subject: [PATCH 32/42] added beam status field to EmissionSystemBeamRecord --- opendis/dis7.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index 1fa0736..f6508cf 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -3605,16 +3605,17 @@ class BeamStatus: """ def __init__(self, beamState: struct8 = 0): - self.beamState = beamState + self.beamState: uint8 = beamState & 0x01 """First bit zero means beam is active, first bit = 1 means deactivated. The rest is padding.""" def serialize(self, outputStream): """serialize the class""" - outputStream.write_unsigned_byte(self.beamState) + outputStream.write_unsigned_byte(self.beamState & 0x01) def parse(self, inputStream): """Parse a message. This may recursively call embedded objects.""" - self.beamState = inputStream.read_unsigned_byte() + value = inputStream.read_unsigned_byte() & 0x01 + self.beamState = value class EnvironmentGeneral: @@ -5630,7 +5631,7 @@ def __init__(self, fundamentalParameterData: "EEFundamentalParameterData | None" = None, beamFunction: enum8 = 0, # [UID 78] highDensityTrackJam: enum8 = 0, # [UID 79] - # MISSING: beam status (See EEPDU 7.6.2) + beamStatus: "BeamStatus | None" = None, jammingModeSequence: "JammingTechnique | None" = None, trackJamRecords: list["TrackJamData"] | None = None): self.beamDataLength = beamDataLength @@ -5640,6 +5641,7 @@ def __init__(self, or EEFundamentalParameterData()) self.beamFunction = beamFunction self.highDensityTrackJam = highDensityTrackJam + self.beamStatus = beamStatus or BeamStatus() self.jammingModeSequence = jammingModeSequence or JammingTechnique() self.trackJamRecords = trackJamRecords or [] @@ -5655,7 +5657,7 @@ def serialize(self, outputStream): outputStream.write_unsigned_byte(self.beamFunction) outputStream.write_unsigned_byte(self.numberOfTargetsInTrackJam) outputStream.write_unsigned_byte(self.highDensityTrackJam) - outputStream.write_unsigned_byte(0) # 8 bit padding + self.beamStatus.serialize(outputStream) outputStream.write_unsigned_int(self.jammingModeSequence) for anObj in self.trackJamRecords: @@ -5669,7 +5671,7 @@ def parse(self, inputStream): self.beamFunction = inputStream.read_unsigned_byte() numberOfTargetsInTrackJam = inputStream.read_unsigned_byte() self.highDensityTrackJam = inputStream.read_unsigned_byte() - inputStream.read_unsigned_byte() # 8 bit padding + self.beamStatus.parse(inputStream) self.jammingModeSequence = inputStream.read_unsigned_int() for idx in range(0, numberOfTargetsInTrackJam): From 0b68e3ea9e3c63b1092d2f060ca4ca936b4c5214 Mon Sep 17 00:00:00 2001 From: 8Oldr <8Oldr@protonmail.com> Date: Thu, 11 Sep 2025 23:40:29 +0300 Subject: [PATCH 33/42] AggregateStatePDU ground work --- opendis/PduFactory.py | 1 + opendis/dis7.py | 134 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/opendis/PduFactory.py b/opendis/PduFactory.py index 881381d..27406cd 100644 --- a/opendis/PduFactory.py +++ b/opendis/PduFactory.py @@ -38,6 +38,7 @@ , 29 : UaPdu , 31 : IntercomSignalPdu , 32 : IntercomControlPdu + , 33 : AggregateStatePdu , 36 : IsPartOfPdu , 37 : MinefieldStatePdu , 40 : MinefieldResponseNackPdu diff --git a/opendis/dis7.py b/opendis/dis7.py index f6508cf..d412531 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -2591,6 +2591,30 @@ def parse(self, inputStream): self.appearanceRecordList.append(element) +class SilentAggregateSystem: + """No section, only referenced in connection with the AggregateStatePDU + + Information about subaggregates not producing Aggregate State PDUs. + """ + + def __init__(self, + numberOfAggregates: uint16 = 0, + aggregateType: "AggregateType | None" = None): + self.numberOfAggregates = numberOfAggregates + """number of the type specified by the aggregate type field""" + self.aggregateType = aggregateType or AggregateType() + + def serialize(self, outputStream): + """serialize the class""" + outputStream.write_unsigned_short(self.numberOfAggregates) + self.entityType.serialize(outputStream) + + def parse(self, inputStream): + """Parse a message. This may recursively call embedded objects.""" + self.numberOfAggregates = inputStream.read_unsigned_short() + self.aggregateType.parse(inputStream) + + class EventIdentifier: """Section 6.2.34 @@ -7859,3 +7883,113 @@ def parse(self, inputStream): self.partLocation.parse(inputStream) self.namedLocationID.parse(inputStream) self.partEntityType.parse(inputStream) + + +class AggregateStatePdu(EntityManagementFamilyPdu): + """Section 7.8.2 + + Detailed information about aggregating entities and communicating + information about the aggregated entities is communicated by this PDU + INCOMPLETE + """ + pduType: enum8 = 33 + + def __init__(self, aggregateID: "AggregateIdentifier | None" = None, + forceID: enum16 = 0, + aggregateState: enum8 = 0, + aggregateType: "AggregateType | None" = None, + formation: enum32 = 0, + aggregateMarking: "AggregateMarking | None" = None, + dimensions: "Vector3Float | None" = None, + orientation: "EulerAngles | None" = None, + centerOfMass: "Vector3Float | None" = None, + velocity: "Vector3Double | None" = None, + numberOfAggregateIDs: uint16 = 0, + numberOfEntityIDs: uint16 = 0, + numberOfSilentAggregateSystems: uint16 = 0, + numberOfSilentEntitySystems: uint16 = 0, + aggregateIDs: list["AggregateIdentifier"] | None = None, + entityIDs: list["EntityIdentifier"] | None = None, + silentAggregateSystems: list["SilentAggregateSystem"] | None = None, + silentEntitySystems: list["SilentEntitySystem"] | None = None, + numberOfVariableDatumRecords: uint32 = 0, + variableDatumRecords: list["VariableDatum"] | None = None): + super(AggregateStatePdu, self).__init__() + """Identifier of the aggregate issuing the PDU""" + self.aggregateID = aggregateID or AggregateIdentifier() + """Common force to which the aggregate belongs""" + self.forceID = forceID + self.aggregateState = aggregateState + self.aggregateType = aggregateType or AggregateType() + self.formation = formation + """Unique marking associated with the aggregate""" + self.aggregateMarking = aggregateMarking or AggregateMarking() + """Bounding space, in meters, occupied by the aggregate""" + self.dimensions = dimensions or Vector3Float() + """Orientation of the aggregate, average of the orientations of the constituents""" + self.orientation = orientation or EulerAngles() + self.centerOfMass = centerOfMass or Vector3Float() + """Aggregates linear velocity. The coordinate system is dependent on the dead reckoning algorithm""" + self.velocity = velocity or Vector3Double() + self.numberOfAggregateIDs = numberOfAggregateIDs + self.numberOfEntityIDs = numberOfEntityIDs + self.numberOfSilentAggregateSystems = numberOfSilentAggregateSystems + self.numberOfSilentEntitySystems = numberOfSilentEntitySystems + """Identify subaggregates that are transmitting Aggregate State PDUs""" + self.aggregateIDs = aggregateIDs or [] + """Constituent entities transmitting Entity State PDUs""" + self.entityIDs = entityIDs or [] + """Subaggregates not producing Aggregate State PDUs""" + self.silentAggregateSystems = silentAggregateSystems or [] + """Entities not producing Entity State PDUs""" + self.silentEntitySystems = silentEntitySystems or [] + self.numberOfVariableDatumRecords = numberOfVariableDatumRecords + self.variableDatumRecords = variableDatumRecords or [] + + def serialize(self, outputStream): + """serialize the class""" + super(AggregateStatePdu, self).serialize(outputStream) + self.aggregateID.serialize(outputStream) + self.forceID.serialize(outputStream) + self.aggregateState.serialize(outputStream) + self.aggregateType.serialize(outputStream) + self.formation.serialize(outputStream) + self.aggregateMarking.serialize(outputStream) + self.dimensions.serialize(outputStream) + self.orientation.serialize(outputStream) + self.centerOfMass.serialize(outputStream) + self.velocity.serialize(outputStream) + self.numberOfAggregateIDs.serialize(outputStream) + self.numberOfEntityIDs.serialize(outputStream) + self.numberOfSilentAggregateSystems.serialize(outputStream) + self.numberOfSilentEntitySystems.serialize(outputStream) + self.aggregateIDs.serialize(outputStream) + self.entityIDs.serialize(outputStream) + self.silentAggregateSystems.serialize(outputStream) + self.silentEntitySystems.serialize(outputStream) + self.numberOfVariableDatumRecords.serialize(outputStream) + self.variableDatumRecords.serialize(outputStream) + + def parse(self, inputStream): + """Parse a message. This may recursively call embedded objects.""" + super(AggregateStatePdu, self).parse(inputStream) + self.aggregateID.parse(inputStream) + self.forceID.parse(inputStream) + self.aggregateState.parse(inputStream) + self.aggregateType.parse(inputStream) + self.formation.parse(inputStream) + self.aggregateMarking.parse(inputStream) + self.dimensions.parse(inputStream) + self.orientation.parse(inputStream) + self.centerOfMass.parse(inputStream) + self.velocity.parse(inputStream) + self.numberOfAggregateIDs.parse(inputStream) + self.numberOfEntityIDs.parse(inputStream) + self.numberOfSilentAggregateSystems.parse(inputStream) + self.numberOfSilentEntitySystems.parse(inputStream) + self.aggregateIDs.parse(inputStream) + self.entityIDs.parse(inputStream) + self.silentAggregateSystems.parse(inputStream) + self.silentEntitySystems.parse(inputStream) + self.numberOfVariableDatumRecords.parse(inputStream) + self.variableDatumRecords.parse(inputStream) From 54e4fdac5e8244afe2a50b0761b1912da0938f64 Mon Sep 17 00:00:00 2001 From: 8Oldr <8Oldr@protonmail.com> Date: Sat, 13 Sep 2025 23:41:40 +0300 Subject: [PATCH 34/42] Serialisation should now be correctly handled, with regards to the different types of properties --- opendis/dis7.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index d412531..dacd3a3 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -7947,31 +7947,39 @@ def __init__(self, aggregateID: "AggregateIdentifier | None" = None, self.variableDatumRecords = variableDatumRecords or [] def serialize(self, outputStream): - """serialize the class""" + """serialize the class: TODO anything that is not a custom type needs outputStream.write_unsigned_bytes (or + equivalent), since it is not native to int for example""" super(AggregateStatePdu, self).serialize(outputStream) self.aggregateID.serialize(outputStream) - self.forceID.serialize(outputStream) - self.aggregateState.serialize(outputStream) + outputStream.write_unsigned_byte(self.forceID) + outputStream.write_unsigned_byte(self.aggregateState) self.aggregateType.serialize(outputStream) - self.formation.serialize(outputStream) + outputStream.write_unsigned_int(self.formation) self.aggregateMarking.serialize(outputStream) self.dimensions.serialize(outputStream) self.orientation.serialize(outputStream) self.centerOfMass.serialize(outputStream) self.velocity.serialize(outputStream) - self.numberOfAggregateIDs.serialize(outputStream) - self.numberOfEntityIDs.serialize(outputStream) - self.numberOfSilentAggregateSystems.serialize(outputStream) - self.numberOfSilentEntitySystems.serialize(outputStream) - self.aggregateIDs.serialize(outputStream) - self.entityIDs.serialize(outputStream) - self.silentAggregateSystems.serialize(outputStream) - self.silentEntitySystems.serialize(outputStream) - self.numberOfVariableDatumRecords.serialize(outputStream) + outputStream.write_unsigned_short(self.numberOfAggregateIDs) + outputStream.write_unsigned_short(self.numberOfEntityIDs) + outputStream.write_unsigned_short(self.numberOfSilentAggregateSystems) + outputStream.write_unsigned_short(self.numberOfSilentEntitySystems) + for aggregateID in self.aggregateIDs: + aggregateID.serialize(outputStream) + for entityID in self.entityIDs: + entityID.serialize(outputStream) + for silentAggregateSystem in self.silentAggregateSystems: + silentAggregateSystem.serialize(outputStream) + for silentEntitySystem in self.silentEntitySystems: + silentEntitySystem.serialize(outputStream) + outputStream.write_unsigned_int(self.numberOfVariableDatumRecords) self.variableDatumRecords.serialize(outputStream) + for variableDatumRecord in self.variableDatumRecords: + variableDatumRecord.serialize(outputStream) def parse(self, inputStream): - """Parse a message. This may recursively call embedded objects.""" + """Parse a message. This may recursively call embedded objects.: TODO anything that is not a custom type needs + inputStream.read_unsigned_byte (or equivalent), since it is not native to int for example""" super(AggregateStatePdu, self).parse(inputStream) self.aggregateID.parse(inputStream) self.forceID.parse(inputStream) From 50033eaadc2ae1d8d6999c7459379c8c227871ed Mon Sep 17 00:00:00 2001 From: 8Oldr <8Oldr@protonmail.com> Date: Sun, 14 Sep 2025 00:03:21 +0300 Subject: [PATCH 35/42] Parsing is correctly handled, but need to switch to @property for the numberOf elements --- opendis/dis7.py | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index dacd3a3..92ff59f 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -7891,6 +7891,8 @@ class AggregateStatePdu(EntityManagementFamilyPdu): Detailed information about aggregating entities and communicating information about the aggregated entities is communicated by this PDU INCOMPLETE + + TODO create properties for numberOfs, and read them to local vars (that are discarded) in parse """ pduType: enum8 = 33 @@ -7973,7 +7975,6 @@ def serialize(self, outputStream): for silentEntitySystem in self.silentEntitySystems: silentEntitySystem.serialize(outputStream) outputStream.write_unsigned_int(self.numberOfVariableDatumRecords) - self.variableDatumRecords.serialize(outputStream) for variableDatumRecord in self.variableDatumRecords: variableDatumRecord.serialize(outputStream) @@ -7982,22 +7983,38 @@ def parse(self, inputStream): inputStream.read_unsigned_byte (or equivalent), since it is not native to int for example""" super(AggregateStatePdu, self).parse(inputStream) self.aggregateID.parse(inputStream) - self.forceID.parse(inputStream) - self.aggregateState.parse(inputStream) + self.forceID = inputStream.read_unsigned_byte() + self.aggregateState = inputStream.read_unsigned_byte() self.aggregateType.parse(inputStream) - self.formation.parse(inputStream) + self.formation = inputStream.read_unsigned_int() self.aggregateMarking.parse(inputStream) self.dimensions.parse(inputStream) self.orientation.parse(inputStream) self.centerOfMass.parse(inputStream) self.velocity.parse(inputStream) - self.numberOfAggregateIDs.parse(inputStream) - self.numberOfEntityIDs.parse(inputStream) - self.numberOfSilentAggregateSystems.parse(inputStream) - self.numberOfSilentEntitySystems.parse(inputStream) + self.numberOfAggregateIDs = inputStream.read_unsigned_short() + self.numberOfEntityIDs = inputStream.read_unsigned_short() + self.numberOfSilentAggregateSystems = inputStream.read_unsigned_short() + self.numberOfSilentEntitySystems = inputStream.read_unsigned_short() self.aggregateIDs.parse(inputStream) - self.entityIDs.parse(inputStream) - self.silentAggregateSystems.parse(inputStream) - self.silentEntitySystems.parse(inputStream) - self.numberOfVariableDatumRecords.parse(inputStream) - self.variableDatumRecords.parse(inputStream) + for idx in range(0, self.numberOfAggregateIDs): # TODO replace with local + element = AggregateIdentifier() + element.parse(inputStream) + self.aggregateIDs.append(element) + for idx in range(0, self.numberOfEntityIDs): # TODO replace with local + element = EntityID() + element.parse(inputStream) + self.entityIDs.append(element) + for idx in range(0, self.numberOfSilentAggregateSystems): # TODO replace with local + element = SilentAggregateSystem() + element.parse(inputStream) + self.silentAggregateSystems.append(element) + for idx in range(0, self.numberOfSilentEntitySystems): # TODO replace with local + element = SilentEntitySystem() + element.parse(inputStream) + self.silentEntitySystems.append(element) + self.numberOfVariableDatumRecords = inputStream.read_unsigned_int() + for idx in range(0, self.numberOfVariableDatumRecords): # TODO replace with local + element = VariableDatum() + element.parse(inputStream) + self.variableDatumRecords.append(element) From e956906b6f45c43b1ed115d54946c4093e849b25 Mon Sep 17 00:00:00 2001 From: 8Oldr <8Oldr@protonmail.com> Date: Sun, 14 Sep 2025 00:10:40 +0300 Subject: [PATCH 36/42] Handling the number of element x properties as @property, instead of values inside the class --- opendis/dis7.py | 59 +++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index 92ff59f..0f4d2e5 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -7891,8 +7891,6 @@ class AggregateStatePdu(EntityManagementFamilyPdu): Detailed information about aggregating entities and communicating information about the aggregated entities is communicated by this PDU INCOMPLETE - - TODO create properties for numberOfs, and read them to local vars (that are discarded) in parse """ pduType: enum8 = 33 @@ -7906,15 +7904,10 @@ def __init__(self, aggregateID: "AggregateIdentifier | None" = None, orientation: "EulerAngles | None" = None, centerOfMass: "Vector3Float | None" = None, velocity: "Vector3Double | None" = None, - numberOfAggregateIDs: uint16 = 0, - numberOfEntityIDs: uint16 = 0, - numberOfSilentAggregateSystems: uint16 = 0, - numberOfSilentEntitySystems: uint16 = 0, aggregateIDs: list["AggregateIdentifier"] | None = None, entityIDs: list["EntityIdentifier"] | None = None, silentAggregateSystems: list["SilentAggregateSystem"] | None = None, silentEntitySystems: list["SilentEntitySystem"] | None = None, - numberOfVariableDatumRecords: uint32 = 0, variableDatumRecords: list["VariableDatum"] | None = None): super(AggregateStatePdu, self).__init__() """Identifier of the aggregate issuing the PDU""" @@ -7933,10 +7926,6 @@ def __init__(self, aggregateID: "AggregateIdentifier | None" = None, self.centerOfMass = centerOfMass or Vector3Float() """Aggregates linear velocity. The coordinate system is dependent on the dead reckoning algorithm""" self.velocity = velocity or Vector3Double() - self.numberOfAggregateIDs = numberOfAggregateIDs - self.numberOfEntityIDs = numberOfEntityIDs - self.numberOfSilentAggregateSystems = numberOfSilentAggregateSystems - self.numberOfSilentEntitySystems = numberOfSilentEntitySystems """Identify subaggregates that are transmitting Aggregate State PDUs""" self.aggregateIDs = aggregateIDs or [] """Constituent entities transmitting Entity State PDUs""" @@ -7945,12 +7934,30 @@ def __init__(self, aggregateID: "AggregateIdentifier | None" = None, self.silentAggregateSystems = silentAggregateSystems or [] """Entities not producing Entity State PDUs""" self.silentEntitySystems = silentEntitySystems or [] - self.numberOfVariableDatumRecords = numberOfVariableDatumRecords self.variableDatumRecords = variableDatumRecords or [] + @property + def numberOfAggregateIDs(self) -> uint16: + return len(self.aggregateIDs) + + @property + def numberOfEntityIDs(self) -> uint16: + return len(self.entityIDs) + + @property + def numberOfSilentAggregateSystems(self) -> uint16: + return len(self.silentAggregateSystems) + + @property + def numberOfSilentEntitySystems(self) -> uint16: + return len(self.silentEntitySystems) + + @property + def numberOfVariableDatumRecords(self) -> uint32: + return len(self.variableDatumRecords) + def serialize(self, outputStream): - """serialize the class: TODO anything that is not a custom type needs outputStream.write_unsigned_bytes (or - equivalent), since it is not native to int for example""" + """serialize the class""" super(AggregateStatePdu, self).serialize(outputStream) self.aggregateID.serialize(outputStream) outputStream.write_unsigned_byte(self.forceID) @@ -7979,8 +7986,7 @@ def serialize(self, outputStream): variableDatumRecord.serialize(outputStream) def parse(self, inputStream): - """Parse a message. This may recursively call embedded objects.: TODO anything that is not a custom type needs - inputStream.read_unsigned_byte (or equivalent), since it is not native to int for example""" + """Parse a message. This may recursively call embedded objects.""" super(AggregateStatePdu, self).parse(inputStream) self.aggregateID.parse(inputStream) self.forceID = inputStream.read_unsigned_byte() @@ -7992,29 +7998,28 @@ def parse(self, inputStream): self.orientation.parse(inputStream) self.centerOfMass.parse(inputStream) self.velocity.parse(inputStream) - self.numberOfAggregateIDs = inputStream.read_unsigned_short() - self.numberOfEntityIDs = inputStream.read_unsigned_short() - self.numberOfSilentAggregateSystems = inputStream.read_unsigned_short() - self.numberOfSilentEntitySystems = inputStream.read_unsigned_short() - self.aggregateIDs.parse(inputStream) - for idx in range(0, self.numberOfAggregateIDs): # TODO replace with local + numberOfAggregateIDs = inputStream.read_unsigned_short() + numberOfEntityIDs = inputStream.read_unsigned_short() + numberOfSilentAggregateSystems = inputStream.read_unsigned_short() + numberOfSilentEntitySystems = inputStream.read_unsigned_short() + for idx in range(0, numberOfAggregateIDs): element = AggregateIdentifier() element.parse(inputStream) self.aggregateIDs.append(element) - for idx in range(0, self.numberOfEntityIDs): # TODO replace with local + for idx in range(0, numberOfEntityIDs): element = EntityID() element.parse(inputStream) self.entityIDs.append(element) - for idx in range(0, self.numberOfSilentAggregateSystems): # TODO replace with local + for idx in range(0, numberOfSilentAggregateSystems): element = SilentAggregateSystem() element.parse(inputStream) self.silentAggregateSystems.append(element) - for idx in range(0, self.numberOfSilentEntitySystems): # TODO replace with local + for idx in range(0, numberOfSilentEntitySystems): element = SilentEntitySystem() element.parse(inputStream) self.silentEntitySystems.append(element) - self.numberOfVariableDatumRecords = inputStream.read_unsigned_int() - for idx in range(0, self.numberOfVariableDatumRecords): # TODO replace with local + numberOfVariableDatumRecords = inputStream.read_unsigned_int() + for idx in range(0, numberOfVariableDatumRecords): element = VariableDatum() element.parse(inputStream) self.variableDatumRecords.append(element) From 78f28bdfd6ecb9961d3fc2a2c95bc9bea865e044 Mon Sep 17 00:00:00 2001 From: 8Oldr <8Oldr@protonmail.com> Date: Sun, 14 Sep 2025 13:04:02 +0300 Subject: [PATCH 37/42] Marking AggregateStatePDU as complete, as verified in Wireshark --- opendis/dis7.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index 0f4d2e5..02575b2 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -7890,7 +7890,7 @@ class AggregateStatePdu(EntityManagementFamilyPdu): Detailed information about aggregating entities and communicating information about the aggregated entities is communicated by this PDU - INCOMPLETE + COMPLETE """ pduType: enum8 = 33 @@ -7923,7 +7923,7 @@ def __init__(self, aggregateID: "AggregateIdentifier | None" = None, self.dimensions = dimensions or Vector3Float() """Orientation of the aggregate, average of the orientations of the constituents""" self.orientation = orientation or EulerAngles() - self.centerOfMass = centerOfMass or Vector3Float() + self.centerOfMass = centerOfMass or Vector3Float() """Aggregates linear velocity. The coordinate system is dependent on the dead reckoning algorithm""" self.velocity = velocity or Vector3Double() """Identify subaggregates that are transmitting Aggregate State PDUs""" From 3fc0061bf90b12a5da981ac46ebc9b7f714fb7bf Mon Sep 17 00:00:00 2001 From: Quinn Gavin Date: Tue, 16 Sep 2025 12:03:51 -0700 Subject: [PATCH 38/42] fix serialization issues --- opendis/dis7.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index 02575b2..19a67c1 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -4160,7 +4160,7 @@ class PduStatus: a byte. """ - def __init__(self, pduStatus: struct8 = 0): + def __init__(self, pduStatus: struct8 = b'0'): self.pduStatus = pduStatus """Bit fields. The semantics of the bit fields depend on the PDU type""" @@ -4213,13 +4213,13 @@ def __init__(self, pduStatus: "PduStatus | None" = None): # (See 6.2.67) def serialize(self, outputStream): """serialize the class""" super(Pdu, self).serialize(outputStream) - outputStream.write_unsigned_byte(self.pduStatus) + self.pduStatus.serialize(outputStream) outputStream.write_unsigned_byte(self.padding) def parse(self, inputStream): """Parse a message. This may recursively call embedded objects.""" super(Pdu, self).parse(inputStream) - self.pduStatus = inputStream.read_unsigned_byte() + self.pduStatus.parse(inputStream) self.padding = inputStream.read_unsigned_byte() @@ -5315,10 +5315,10 @@ def __init__(self, entityLinearVelocity: "Vector3Float | None" = None, entityLocation: "Vector3Double | None" = None, entityOrientation: "EulerAngles | None" = None, - entityAppearance: struct32 = 0, # [UID 31-43] + entityAppearance: struct32 = b'0000', # [UID 31-43] deadReckoningParameters: "DeadReckoningParameters | None" = None, marking: "EntityMarking | None" = None, - capabilities: struct32 = 0, # [UID 55] + capabilities: struct32 = b'0000', # [UID 55] variableParameters: list["VariableParameter"] | None = None): super(EntityStatePdu, self).__init__() self.entityID = entityID or EntityID() @@ -5682,7 +5682,7 @@ def serialize(self, outputStream): outputStream.write_unsigned_byte(self.numberOfTargetsInTrackJam) outputStream.write_unsigned_byte(self.highDensityTrackJam) self.beamStatus.serialize(outputStream) - outputStream.write_unsigned_int(self.jammingModeSequence) + self.jammingModeSequence.serialize(outputStream) for anObj in self.trackJamRecords: anObj.serialize(outputStream) @@ -5696,7 +5696,7 @@ def parse(self, inputStream): numberOfTargetsInTrackJam = inputStream.read_unsigned_byte() self.highDensityTrackJam = inputStream.read_unsigned_byte() self.beamStatus.parse(inputStream) - self.jammingModeSequence = inputStream.read_unsigned_int() + self.jammingModeSequence.parse(inputStream) for idx in range(0, numberOfTargetsInTrackJam): element = TrackJamData() From 6812943d1d6af48ca7b34cb5b333568afe440fac Mon Sep 17 00:00:00 2001 From: Quinn Gavin Date: Tue, 16 Sep 2025 13:39:27 -0700 Subject: [PATCH 39/42] fix serialization issues --- opendis/dis7.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index 19a67c1..f34de96 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -4203,9 +4203,9 @@ def parse(self, inputStream): class Pdu(PduSuperclass): """Adds some fields to the the classic PDU""" - def __init__(self, pduStatus: "PduStatus | None" = None): # (See 6.2.67) + def __init__(self, pduStatus: uint8 = 0): # (See 6.2.67) super(Pdu, self).__init__() - self.pduStatus = pduStatus or PduStatus().pduStatus + self.pduStatus: uint8 = pduStatus """PDU Status Record. Described in 6.2.67. This field is not present in earlier DIS versions""" self.padding: uint8 = 0 """zero-filled array of padding""" @@ -4213,13 +4213,13 @@ def __init__(self, pduStatus: "PduStatus | None" = None): # (See 6.2.67) def serialize(self, outputStream): """serialize the class""" super(Pdu, self).serialize(outputStream) - self.pduStatus.serialize(outputStream) + outputStream.write_unsigned_byte(self.pduStatus) outputStream.write_unsigned_byte(self.padding) def parse(self, inputStream): """Parse a message. This may recursively call embedded objects.""" super(Pdu, self).parse(inputStream) - self.pduStatus.parse(inputStream) + self.pduStatus = inputStream.read_unsigned_byte() self.padding = inputStream.read_unsigned_byte() From 73917e97d3d22e1d1effd18e38a2f15788d29095 Mon Sep 17 00:00:00 2001 From: Quinn Gavin Date: Tue, 16 Sep 2025 13:54:46 -0700 Subject: [PATCH 40/42] fix jamming mode issue --- opendis/dis7.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/opendis/dis7.py b/opendis/dis7.py index f34de96..fc73c1e 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -5656,7 +5656,7 @@ def __init__(self, beamFunction: enum8 = 0, # [UID 78] highDensityTrackJam: enum8 = 0, # [UID 79] beamStatus: "BeamStatus | None" = None, - jammingModeSequence: "JammingTechnique | None" = None, + jammingModeSequence: uint32 = 0, trackJamRecords: list["TrackJamData"] | None = None): self.beamDataLength = beamDataLength self.beamIDNumber = beamIDNumber @@ -5666,7 +5666,7 @@ def __init__(self, self.beamFunction = beamFunction self.highDensityTrackJam = highDensityTrackJam self.beamStatus = beamStatus or BeamStatus() - self.jammingModeSequence = jammingModeSequence or JammingTechnique() + self.jammingModeSequence = jammingModeSequence self.trackJamRecords = trackJamRecords or [] @property @@ -5682,7 +5682,7 @@ def serialize(self, outputStream): outputStream.write_unsigned_byte(self.numberOfTargetsInTrackJam) outputStream.write_unsigned_byte(self.highDensityTrackJam) self.beamStatus.serialize(outputStream) - self.jammingModeSequence.serialize(outputStream) + outputStream.write_unsigned_int(self.jammingModeSequence) for anObj in self.trackJamRecords: anObj.serialize(outputStream) @@ -5696,7 +5696,7 @@ def parse(self, inputStream): numberOfTargetsInTrackJam = inputStream.read_unsigned_byte() self.highDensityTrackJam = inputStream.read_unsigned_byte() self.beamStatus.parse(inputStream) - self.jammingModeSequence.parse(inputStream) + self.jammingModeSequence = inputStream.read_unsigned_int() for idx in range(0, numberOfTargetsInTrackJam): element = TrackJamData() From 5d3e14185137f1b130ff73a8221af450c0c5ddf9 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sat, 18 May 2024 14:38:13 +0000 Subject: [PATCH 41/42] [docs] TransmitterPdu comments cleanup --- opendis/dis7.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opendis/dis7.py b/opendis/dis7.py index fc73c1e..bff5008 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -5482,6 +5482,7 @@ def __init__(self, self.radioEntityType = radioEntityType or EntityType() # TODO: validation self.transmitState = transmitState self.inputSource = inputSource + self.variableTransmitterParameterCount = variableTransmitterParameterCount self.antennaLocation = antennaLocation or Vector3Double() self.relativeAntennaLocation = relativeAntennaLocation or Vector3Float( ) From 396abb251f7fc8f7ece118bfff18033d5e1f77b4 Mon Sep 17 00:00:00 2001 From: JS Ng <45561895+ngjunsiang@users.noreply.github.com> Date: Sun, 19 May 2024 03:39:20 +0000 Subject: [PATCH 42/42] [fix] SignalPdu correctly parses data samples This was missed in an earlier refactor that converted length/count attributes into properties. --- opendis/dis7.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/opendis/dis7.py b/opendis/dis7.py index bff5008..5cee1b2 100644 --- a/opendis/dis7.py +++ b/opendis/dis7.py @@ -7498,6 +7498,8 @@ def parse(self, inputStream): self.tdlType = inputStream.read_unsigned_short() self.sampleRate = inputStream.read_unsigned_int() dataLength = inputStream.read_unsigned_short() + # TODO: Make validation optional + assert dataLength % 8 == 0 self.samples = inputStream.read_unsigned_short() for idx in range(0, dataLength // 8): element = inputStream.read_unsigned_byte()