Skip to content

Commit 259cb5f

Browse files
Merge pull request #4 from NTIA/dev
Initial release of `lfmf-python`
2 parents d143aca + fc207bf commit 259cb5f

13 files changed

+216
-48
lines changed

.github/workflows/cff-validator.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ name: Validate CITATION.cff
22

33
on:
44
push:
5+
branches: ["main", "dev"]
56
paths:
67
- 'CITATION.cff'
78
- '.github/workflows/cff-validator.yml'
89
pull_request:
10+
branches: ["main", "dev"]
911
paths:
1012
- 'CITATION.cff'
1113
- '.github/workflows/cff-validator.yml'
@@ -22,6 +24,4 @@ jobs:
2224
- name: Checkout repository
2325
uses: actions/checkout@v4
2426
- name: Validate CITATION.cff
25-
uses: dieghernan/cff-validator@v3
26-
with:
27-
install-r: true
27+
uses: dieghernan/cff-validator@v4

.github/workflows/pytest.yml

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: Test with pytest
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
pull_request:
9+
branches:
10+
- main
11+
- dev
12+
13+
concurrency:
14+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
15+
cancel-in-progress: true
16+
17+
env:
18+
LIBRARY_BASE_REPO: NTIA/LFMF
19+
LIBRARY_RELEASE_TAG: v1.1
20+
LIBRARY_DESTINATION_DIRECTORY: 'src/ITS/Propagation/LFMF'
21+
22+
jobs:
23+
run-all-tests:
24+
name: ${{ matrix.platform.os-name }} / Py${{ matrix.py }}
25+
runs-on: ${{ matrix.platform.os-runner }}
26+
strategy:
27+
fail-fast: false
28+
matrix:
29+
platform:
30+
- os-name: 'Windows (64-bit)'
31+
os-runner: 'windows-latest'
32+
arch-id: 'x64'
33+
release-file-pattern: '*-x64.dll'
34+
- os-name: 'Windows (32-bit)'
35+
os-runner: 'windows-latest'
36+
arch-id: 'x86'
37+
release-file-pattern: '*-x86.dll'
38+
- os-name: 'macOS (intel/x64)'
39+
os-runner: 'macos-13'
40+
arch-id: 'x64'
41+
release-file-pattern: '*.dylib'
42+
- os-name: 'macOS (apple/arm64)'
43+
os-runner: 'macos-latest'
44+
arch-id: 'arm64'
45+
release-file-pattern: '*.dylib'
46+
- os-name: 'Linux (Ubuntu)'
47+
os-runner: 'ubuntu-latest'
48+
arch-id: 'x64'
49+
release-file-pattern: '*.so'
50+
py: # Python versions to test on all platforms
51+
- "3.9"
52+
- "3.10"
53+
- "3.11"
54+
- "3.12"
55+
steps:
56+
- name: Check out repository
57+
uses: actions/checkout@v4
58+
with:
59+
submodules: true
60+
61+
# Cache key is unique to the combination of runner OS + architecture (matrix.arch-id) + release tag
62+
- name: Restore ${{ env.LIBRARY_RELEASE_TAG }} binaries from cache if available
63+
id: cache-restore
64+
uses: actions/cache@v4
65+
with:
66+
key: ${{ runner.os }}-${{ matrix.platform.arch-id }}-${{ env.LIBRARY_RELEASE_TAG }}
67+
path: ${{ env.LIBRARY_DESTINATION_DIRECTORY}}/${{ matrix.platform.release-file-pattern }}
68+
69+
# Only the binaries required for the current platform are downloaded. Note that the distributed
70+
# wheel for proplib python packages includes all binaries, so that the wheel is inherently cross-platform.
71+
- name: Download required ${{ env.LIBRARY_RELEASE_TAG }} binaries
72+
if: ${{ steps.cache-restore.outputs.cache-hit != 'true' }}
73+
uses: robinraju/release-downloader@v1
74+
with:
75+
repository: ${{ env.LIBRARY_BASE_REPO }}
76+
tag: ${{ env.LIBRARY_RELEASE_TAG }}
77+
fileName: ${{ matrix.platform.release-file-pattern }}
78+
tarBall: false
79+
zipBall: false
80+
out-file-path: ${{ env.LIBRARY_DESTINATION_DIRECTORY }}
81+
82+
- name: Set up Python ${{ matrix.py }}
83+
uses: actions/setup-python@v5
84+
with:
85+
architecture: ${{ matrix.platform.arch-id }}
86+
python-version: ${{ matrix.py }}
87+
cache: 'pip'
88+
89+
- name: Install dependencies for testing
90+
run: python -m pip install -e .[tests]
91+
92+
- name: Run pytest
93+
run: pytest --cov-report=term-missing --no-cov-on-fail --cov

.github/workflows/release.yml

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Action builds a universal (Win32/Win64/macOS-universal/Linux-x64) Python wheel
2+
# from the source code, using Hatchling, and uploads it as an artifact. An sdist (.tar.gz) is
3+
# also uploaded, which includes all platform shared library files. These artifacts should be
4+
# used when creating new releases on PyPI and GitHub. The action is triggered by pushes into `main`
5+
# or pull_requests into `main` or `dev` (for testing). To aid in releases, the workflow is
6+
# also triggered when new SemVer tags are created.
7+
name: Build Release Artifacts
8+
9+
on:
10+
workflow_dispatch:
11+
push:
12+
branches:
13+
- main
14+
tags:
15+
- 'v[0-9]+.*'
16+
pull_request:
17+
branches:
18+
- main
19+
- dev
20+
21+
env:
22+
LIBRARY_BASE_REPO: NTIA/LFMF
23+
LIBRARY_RELEASE_TAG: v1.1
24+
LIBRARY_DESTINATION_DIRECTORY: 'src/ITS/Propagation/LFMF/'
25+
26+
jobs:
27+
build_wheel:
28+
name: Build a universal, cross-platform wheel
29+
runs-on: ubuntu-latest
30+
steps:
31+
- name: Check out repository
32+
uses: actions/checkout@v4
33+
with:
34+
submodules: true
35+
36+
# Only the binaries required for the current platform are downloaded. Note that the distributed
37+
# wheel for proplib python packages includes all binaries, so that the wheel is inherently cross-platform.
38+
- name: Download required ${{ env.LIBRARY_RELEASE_TAG }} Windows binaries
39+
uses: robinraju/release-downloader@v1
40+
with:
41+
repository: ${{ env.LIBRARY_BASE_REPO }}
42+
tag: ${{ env.LIBRARY_RELEASE_TAG }}
43+
fileName: '*.dll'
44+
tarBall: false
45+
zipBall: false
46+
out-file-path: ${{ env.LIBRARY_DESTINATION_DIRECTORY }}
47+
48+
- name: Download required ${{ env.LIBRARY_RELEASE_TAG }} Linux binaries
49+
uses: robinraju/release-downloader@v1
50+
with:
51+
repository: ${{ env.LIBRARY_BASE_REPO }}
52+
tag: ${{ env.LIBRARY_RELEASE_TAG }}
53+
fileName: '*.so'
54+
tarBall: false
55+
zipBall: false
56+
out-file-path: ${{ env.LIBRARY_DESTINATION_DIRECTORY }}
57+
58+
- name: Download required ${{ env.LIBRARY_RELEASE_TAG }} macOS binaries
59+
uses: robinraju/release-downloader@v1
60+
with:
61+
repository: ${{ env.LIBRARY_BASE_REPO }}
62+
tag: ${{ env.LIBRARY_RELEASE_TAG }}
63+
fileName: '*.dylib'
64+
tarBall: false
65+
zipBall: false
66+
out-file-path: ${{ env.LIBRARY_DESTINATION_DIRECTORY }}
67+
68+
- name: Set up Python
69+
uses: actions/setup-python@v5
70+
with:
71+
python-version: '3.13'
72+
73+
- name: Install build dependencies
74+
run: pip install hatchling
75+
76+
- name: Build wheels
77+
run: hatchling build
78+
79+
- uses: actions/upload-artifact@v4
80+
with:
81+
name: Release Artifacts (sdist and wheel)
82+
path: dist/*

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,4 @@ cython_debug/
164164
# and can be added to the global gitignore or merged into this file. For a more nuclear
165165
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
166166
#.idea/
167+
.idea/

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "tests/data"]
2+
path = tests/data
3+
url = https://github.com/NTIA/LFMF-test-data

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ repos:
1616
- id: end-of-file-fixer
1717
- id: trailing-whitespace
1818
- repo: https://github.com/asottile/pyupgrade
19-
rev: v3.19.0
19+
rev: v3.19.1
2020
hooks:
2121
- id: pyupgrade
2222
args: ["--py39-plus"]

.zenodo.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"upload_type": "software",
3-
"publication_date": "TODO-TEMPLATE",
3+
"publication_date": "2025-1-23",
44
"title": "Low Frequency / Medium Frequency (LF/MF) Propagation Model, Python Wrapper",
55
"creators": [
66
{
@@ -15,7 +15,6 @@
1515
}
1616
],
1717
"description": "This code repository contains a Python wrapper for the NTIA/ITS implementation of the Low Frequency / Medium Frequency (LF/MF) Propagation Model.",
18-
"access_right": "open",
1918
"keywords": [
2019
"propagation",
2120
"communications",
@@ -36,7 +35,7 @@
3635
{
3736
"identifier": "https://ntia.github.io/propagation-library-wiki/models/LFMF/",
3837
"relation": "isDocumentedBy",
39-
"resource_type": "softwaredocumentation"
38+
"resource_type": "publication-softwaredocumentation"
4039
}
4140
],
4241
"version": "1.1.0"

GitHubRepoPublicReleaseApproval.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@
77
The project identified above, which is contained within the repository this
88
document is stored in, has met the following criteria for public release:
99

10-
1. [ ] The project, including the test criteria, meets the requirements defined
10+
1. [x] The project, including the test criteria, meets the requirements defined
1111
in the ITS Software Development Publication Policy for making a repository public.
1212
The major pre-established criteria for publication are listed below, and the check
1313
mark next to each attests that the criterion has been met.
14-
* [ ] Unit tests are available and the software has been tested against the unit tests.
15-
* [ ] The software can be compiled and/or used on Windows, macOS, and Linux.
16-
* [ ] The repository structure and contents follow from the ITS PropLib template, and
14+
* [x] Unit tests are available and the software has been tested against the unit tests.
15+
* [x] The software can be compiled and/or used on Windows, macOS, and Linux.
16+
* [x] The repository structure and contents follow from the ITS PropLib template, and
1717
all template or placeholder code has been removed.
18-
* [ ] The repository includes the appropriate `LICENSE.md` file
19-
2. [ ] Any test data necessary for the code and its unit tests to function is included in this
18+
* [x] The repository includes the appropriate `LICENSE.md` file
19+
2. [x] Any test data necessary for the code and its unit tests to function is included in this
2020
GitHub repository, either directly or as a linked Git submodule.
21-
3. [ ] The README.md file has passed editorial review from the ITS Publications Office.
22-
4. [ ] The project complies with the ITS Code Style Guide or an appropriate style
21+
3. [x] The README.md file has passed editorial review from the ITS Publications Office.
22+
4. [x] The project complies with the ITS Code Style Guide or an appropriate style
2323
guide as agreed to by the sponsor, project lead, or Supervising Division Chief.
24-
5. [ ] Approved disclaimer and licensing language has been included.
24+
5. [x] Approved disclaimer and licensing language has been included.
2525

2626
In order to complete this approval, please create a new branch, upload and commit
2727
your version of this Markdown document to that branch, then create a pull request

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
# Low Frequency / Medium Frequency (LF/MF) Propagation Model, Python® Wrapper #
22

33
[![NTIA/ITS PropLib][proplib-badge]][proplib-link]
4-
[![GitHub Issues][gh-issues-badge]][gh-issues-link]
54
[![PyPI Release][pypi-release-badge]][pypi-release-link]
65
[![GitHub Actions Unit Test Status][gh-actions-test-badge]][gh-actions-test-link]
6+
[![GitHub Issues][gh-issues-badge]][gh-issues-link]
77
[![DOI][doi-badge]][doi-link]
88

99
[proplib-badge]: https://img.shields.io/badge/PropLib-badge?label=%F0%9F%87%BA%F0%9F%87%B8%20NTIA%2FITS&labelColor=162E51&color=D63E04
1010
[proplib-link]: https://ntia.github.io/propagation-library-wiki
11-
[gh-actions-test-badge]: https://img.shields.io/github/actions/workflow/status/NTIA/LFMF-python/tox.yml?branch=main&logo=pytest&logoColor=ffffff&label=Tests&labelColor=162E51
12-
[gh-actions-test-link]: https://github.com/NTIA/LFMF-python/actions/workflows/tox.yml
11+
[gh-actions-test-badge]: https://img.shields.io/github/actions/workflow/status/NTIA/LFMF-python/pytest.yml?branch=main&logo=pytest&logoColor=ffffff&label=Tests&labelColor=162E51
12+
[gh-actions-test-link]: https://github.com/NTIA/LFMF-python/actions/workflows/pytest.yml
1313
[pypi-release-badge]: https://img.shields.io/pypi/v/proplib-lfmf?logo=pypi&logoColor=ffffff&label=Release&labelColor=162E51&color=D63E04
1414
[pypi-release-link]: https://pypi.org/project/proplib-lfmf
1515
[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/LFMF-python?logo=github&label=Issues&labelColor=162E51

pyproject.toml

+5-25
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ requires = ["hatchling"]
33
build-backend = "hatchling.build"
44

55
[project]
6-
name = "LFMF"
6+
name = "proplib-lfmf"
77
dynamic = ["version"]
88
description = "A Python wrapper for the NTIA/ITS implementation of the Low Frequency / Medium Frequency (LF/MF) Propagation Model"
99
readme = "README.md"
@@ -42,7 +42,6 @@ classifiers = [
4242
tests = [
4343
"pytest>=8.2.0,<9.0",
4444
"pytest-cov>=6.0.0,<7.0",
45-
"tox>=4.21.1,<5.0", # Keep in sync with tool.tox.min_version
4645
]
4746
dev = [
4847
"hatchling>=1.25.0,<2.0",
@@ -51,6 +50,7 @@ dev = [
5150
]
5251

5352
[project.urls]
53+
"PropLib Wiki" = "https://ntia.github.io/propagation-library-wiki/models/LFMF/"
5454
"Python Wrapper Source" = "https://github.com/NTIA/LFMF-python"
5555
"Python Wrapper Bug Tracker" = "https://github.com/NTIA/LFMF-python/issues"
5656
"C++ Source" = "https://github.com/NTIA/LFMF"
@@ -64,29 +64,9 @@ path = "src/ITS/Propagation/LFMF/__init__.py"
6464
packages = ["src/ITS"]
6565
ignore-vcs = true
6666

67+
[tool.hatch.build.targets.sdist]
68+
ignore-vcs = true
69+
6770
[tool.cibuildwheel]
6871
test-command = "pytest ."
6972
test-requires = "pytest"
70-
71-
[tool.tox]
72-
min_version = "4.21.1"
73-
env_list = ["3.9", "3.10", "3.11", "3.12", "3.13"]
74-
skip_missing_interpreters = true
75-
76-
[tool.tox.env.testenv]
77-
description = "Run tests with pytest and generate coverage report"
78-
extras = "tests"
79-
commands = [
80-
"pytest",
81-
"--cov-report=term-missing",
82-
"--no-cov-on-fail",
83-
"--cov",
84-
{ replace = "posargs", extend = true },
85-
]
86-
87-
[tool.tox.gh.python] # tox-gh config for GitHub Actions testing
88-
"3.9" = ["3.9"]
89-
"3.10" = ["3.10"]
90-
"3.11" = ["3.11"]
91-
"3.12" = ["3.12"]
92-
"3.13" = ["3.13"]

src/ITS/Propagation/LFMF/proplib_loader.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"""
4444

4545
import platform
46+
import struct
4647
from ctypes import *
4748
from pathlib import Path
4849

@@ -67,15 +68,24 @@ def get_lib_name(lib_name: str) -> str:
6768
6869
:param lib_name: The library name, with no extension or path, e.g., "P2108-1.0"
6970
:raises NotImplementedError: For platforms other than Windows, Linux, or macOS.
71+
:raises RuntimeError: On Windows, if unable to determine system architecture.
7072
:return: The full filename, including path and extension, of the library.
7173
"""
7274
# Load the compiled library
7375
if platform.uname()[0] == "Windows":
74-
lib_name += ".dll"
76+
arch = struct.calcsize("P") * 8 # 32 or 64
77+
if arch == 64:
78+
lib_name += "-x64.dll"
79+
elif arch == 32:
80+
lib_name += "-x86.dll"
81+
else:
82+
raise RuntimeError(
83+
"Failed to determine system architecture for DLL loading"
84+
)
7585
elif platform.uname()[0] == "Linux":
76-
lib_name += ".so"
86+
lib_name += "-x86_64.so"
7787
elif platform.uname()[0] == "Darwin":
78-
lib_name += ".dylib"
88+
lib_name += "-universal.dylib"
7989
else:
8090
raise NotImplementedError("Your OS is not yet supported")
8191
# Library should be in the same directory as this file

tests/data

Submodule data added at d3cc4d6

tests/test_utils.py

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
# Check if test data directory exists and is not empty
99
if not TEST_DATA_DIR.exists() or not any(TEST_DATA_DIR.iterdir()):
10-
_test_data_checked = True
1110
raise RuntimeError(
1211
f"Test data is not available in {TEST_DATA_DIR}.\n Try running "
1312
+ "`git submodule init` and `git submodule update` to clone the test data submodule."

0 commit comments

Comments
 (0)