Skip to content

Commit e6dcf0c

Browse files
authored
Fix #246: BinaryParameter truncation in xarray dataset creation (#247)
- Add conditional logic in create_dataset to change BinaryParameter objects to bytes objects when creating numpy arrays of byte strings - Add a focused regression test in test_xarr.py:181 - Modify workflow run conditions for tests - Bump version to 6.1.2
1 parent 84d2a76 commit e6dcf0c

10 files changed

Lines changed: 79 additions & 35 deletions

File tree

.github/workflows/_build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
4747
# Save ("upload") the distribution artifacts for use by downstream Actions jobs
4848
- name: Upload distribution artifacts
49-
uses: actions/upload-artifact@v6 # This allows us to persist the dist directory after the job has completed
49+
uses: actions/upload-artifact@v7 # This allows us to persist the dist directory after the job has completed
5050
with:
5151
name: python-package-distributions
5252
path: dist/
@@ -98,7 +98,7 @@ jobs:
9898
9999
# This makes the artifacts available for downstream jobs
100100
- name: Upload Conda build artifact
101-
uses: actions/upload-artifact@v6
101+
uses: actions/upload-artifact@v7
102102
with:
103103
name: conda-package
104104
path: ${{ env.CONDA_BLD_PATH }}/**/space_packet_parser-*

.github/workflows/ci.yml

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ jobs:
2222

2323
# Run unit and integration tests
2424
run-tests:
25-
# Don't run for label additions
26-
if: |
27-
github.event_name != 'pull_request' ||
28-
github.event.action == 'opened' ||
29-
github.event.action == 'synchronize' ||
30-
github.event.action == 'reopened'
3125
name: Test
3226
runs-on: ${{ matrix.os }}
3327
permissions:
@@ -62,18 +56,12 @@ jobs:
6256
run: |
6357
pytest --color=yes --cov --cov-report=xml
6458
65-
- uses: codecov/codecov-action@v5
59+
- uses: codecov/codecov-action@v6
6660
with:
6761
use_oidc: true
6862

6963
# Run the example scripts and ensure there are no errors
7064
run-examples:
71-
# Don't run for label additions
72-
if: |
73-
github.event_name != 'pull_request' ||
74-
github.event.action == 'opened' ||
75-
github.event.action == 'synchronize' ||
76-
github.event.action == 'reopened'
7765
name: Run Examples
7866
runs-on: ubuntu-latest
7967
permissions:

.github/workflows/release.yml

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
steps:
3535
# This downloads the build artifacts from the build job
3636
- name: Download distribution artifacts
37-
uses: actions/download-artifact@v7
37+
uses: actions/download-artifact@v8
3838
with:
3939
name: python-package-distributions
4040
path: dist/
@@ -59,7 +59,7 @@ jobs:
5959
steps:
6060
# This downloads the build artifacts from the build job
6161
- name: Download distribution artifacts
62-
uses: actions/download-artifact@v7
62+
uses: actions/download-artifact@v8
6363
with:
6464
name: python-package-distributions
6565
path: dist/
@@ -80,7 +80,7 @@ jobs:
8080

8181
steps:
8282
- name: Download Conda artifact
83-
uses: actions/download-artifact@v7
83+
uses: actions/download-artifact@v8
8484
with:
8585
name: conda-package
8686
path: conda-package/
@@ -146,12 +146,21 @@ jobs:
146146
GITHUB_TOKEN: ${{ github.token }}
147147
# Uses the GitHub CLI to generate the Release and auto-generate the release notes. Also generates
148148
# the Release title based on the annotation on the git tag.
149-
run: >-
149+
run: |
150150
RELEASE_NAME=$(basename "${{ github.ref_name }}")
151-
gh release create
152-
'${{ github.ref_name }}'
153-
--repo '${{ github.repository }}'
154-
--title "$RELEASE_NAME"
155-
${{ env.PRE_RELEASE_OPTION }}
156-
--generate-notes
157-
--notes-start-tag '${{ env.LATEST_RELEASE_TAG }}'
151+
ARGS=(
152+
"${{ github.ref_name }}"
153+
--repo "${{ github.repository }}"
154+
--title "$RELEASE_NAME"
155+
)
156+
157+
if [ "${{ env.PRE_RELEASE_OPTION }}" = "--prerelease" ]; then
158+
ARGS+=(--prerelease)
159+
fi
160+
161+
ARGS+=(
162+
--generate-notes
163+
--notes-start-tag "${{ env.LATEST_RELEASE_TAG }}"
164+
)
165+
166+
gh release create "${ARGS[@]}"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ build
2626
dist
2727
space_packet_parser/_version.py
2828
uv.lock
29+
node_modules
2930

3031
# Packages #
3132
############

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cff-version: 1.2.0
22
title: 'space_packet_parser'
33
type: software
4-
version: '6.1.1'
4+
version: '6.1.2'
55
description: A CCSDS telemetry packet decoding library based on the XTCE packet format description standard.
66
license: BSD-3-Clause
77
abstract: The Space Packet Parser Python library is a generalized, configurable packet decoding library for CCSDS telemetry

docs/source/changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ list and release milestones.
77

88
Release notes for the `space_packet_parser` library
99

10+
### v6.1.2
11+
12+
- BUGFIX: Prevent BinaryParameter truncation in `create_dataset`. [#246](https://github.com/lasp/space_packet_parser/issues/246)
13+
1014
### v6.1.1
1115

1216
- BUGFIX: Support lxml 5.2.1. [#236](https://github.com/lasp/space_packet_parser/issues/236)

meta.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package:
22
name: "space_packet_parser"
3-
version: "6.1.1"
3+
version: "6.1.2"
44

55
source:
66
path: .

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "space_packet_parser"
3-
version = "6.1.1"
3+
version = "6.1.2"
44
description = "A CCSDS telemetry packet decoding library based on the XTCE packet format description standard."
55
license = { text = "BSD-3-Clause" }
66
readme = "README.md"

space_packet_parser/xarr.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,17 @@ def _process_generator(generator):
255255
dataset_by_apid = {}
256256

257257
for apid, data in data_dict.items():
258-
ds = xr.Dataset(
259-
data_vars={
260-
key: (["packet"], np.asarray(list_of_values, dtype=datatype_mapping[apid][key]))
261-
for key, list_of_values in data.items()
262-
}
263-
)
258+
data_vars: dict[str, tuple[list[str], np.ndarray]] = {} # {var_name: ([dims, ...], data_array)}
259+
for key, list_of_values in data.items():
260+
dtype = np.dtype(datatype_mapping[apid][key])
261+
if dtype.kind == "S":
262+
# Special case for byte strings. np.asarray doesn't process BinaryParameter objects correctly to
263+
# byte strings, so we need to convert them to bytes first before creating the array.
264+
# See: https://github.com/lasp/space_packet_parser/issues/246
265+
list_of_values = [bytes(val) for val in list_of_values]
266+
data_vars[key] = (["packet"], np.asarray(list_of_values, dtype=dtype))
267+
268+
ds = xr.Dataset(data_vars=data_vars)
264269

265270
dataset_by_apid[apid] = ds
266271

tests/unit/test_xarr.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,43 @@ def test_create_dataset_with_custom_generator(tmp_path, fixed_length_packet_defi
178178
assert list(dataset["INT32_FIELD"].values) == [12345, 67890, -99999]
179179

180180

181+
def test_create_dataset_preserves_binary_parameter_width(tmp_path):
182+
"""Test that binary parameters keep their full byte width in the resulting dataset."""
183+
packet_definition = definitions.XtcePacketDefinition(
184+
container_set=[
185+
containers.SequenceContainer(
186+
"BINARY_CONTAINER",
187+
entry_list=[
188+
parameters.Parameter(
189+
"BIN_FIELD",
190+
parameter_type=parameter_types.BinaryParameterType(
191+
"BIN_TYPE", encoding=encodings.BinaryDataEncoding(fixed_size_in_bits=64)
192+
),
193+
)
194+
],
195+
)
196+
]
197+
)
198+
packet_data = b"ABCDEFGH"
199+
test_file = tmp_path / "binary_packets.bin"
200+
test_file.write_bytes(packet_data)
201+
202+
datasets = xarr.create_dataset(
203+
test_file,
204+
packet_definition,
205+
packet_bytes_generator=fixed_length_generator,
206+
generator_kwargs={"packet_length_bytes": 8},
207+
parse_bytes_kwargs={"root_container_name": "BINARY_CONTAINER"},
208+
)
209+
210+
dataset = list(datasets.values())[0]
211+
212+
assert dataset["BIN_FIELD"].values.dtype.kind == "S" # Should be a bytes/string type
213+
assert dataset["BIN_FIELD"].values.dtype.itemsize == 8
214+
assert dataset["BIN_FIELD"].values.dtype == "|S8"
215+
assert dataset["BIN_FIELD"].values.tolist() == [packet_data]
216+
217+
181218
def test_create_dataset_with_packet_filter(tmp_path, fixed_length_packet_definition, fixed_length_test_packets):
182219
"""Test filtering packets with packet_filter parameter using raw byte inspection"""
183220
_, _, _, binary_data = fixed_length_test_packets

0 commit comments

Comments
 (0)