Skip to content

Commit 41acf8a

Browse files
committed
Preliminary Windows support
Also add more readme instructions
1 parent c693d98 commit 41acf8a

File tree

14 files changed

+55560
-46
lines changed

14 files changed

+55560
-46
lines changed

README.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# python-mscl
22

3-
Unofficial Python bindings for the [Microstrain Communication Library](https://www.microstrain.com/developers/microstrain-communication-library).
3+
Unofficial Python package for the [Microstrain Communication Library](https://github.com/LORD-MicroStrain/MSCL/tree/master).
44

55
This library just makes it so that we can install the MSCL library using pip. Wheels are not provided. This will fetch the necessary files for your architecture and python
66
version, and then build the wheel for you.
@@ -18,11 +18,49 @@ pip install python-mscl
1818
### Usage
1919

2020
```python
21-
import mscl
21+
from python_mscl import mscl
2222

2323
# ... use the MSCL library as you normally would
2424
```
2525

26+
### Windows support:
27+
28+
The latest mscl version (v67.0.0) only has a .zip for python 3.11. It is not confirmed if this will work with other python versions.
29+
30+
31+
### Versioning system:
32+
33+
This repository follows the same versioning system as the MSCL library. This is reflected in the tags of this repository.
34+
35+
The version reflected in PyPI is as follows:
36+
37+
```
38+
<MSCL_VERSION>.<REPO_VERSION>
39+
```
40+
41+
E.g, there could be a version: `67.0.0.3` which would mean that the MSCL version is `67.0.0` and this is the third release of the python-mscl package.
42+
2643
## Local Development:
2744

28-
TODO
45+
The below steps assume you have [`uv`](https://docs.astral.sh/uv/) installed.
46+
47+
1. Clone the repo and `cd` into it.
48+
2. Optional: Create a .env file and insert your GITHUB_TOKEN= to make requests to the GitHub API.
49+
3. Edit & run `uv run main.py` to fetch the latest tagged MSCL releases and extract them.
50+
4. Run `uv build`, which will build the source distribution and wheel for your python
51+
version and architecture.
52+
53+
Notes for me, the maintainer:
54+
5. Optional: Run `uv publish` to publish the package to PyPI. To upload to TestPyPI, uncomment lines in `pyproject.toml`, and run `uv publish --index testpypi dist/*.tar.gz`.
55+
6. Optional: To check if the package worked correctly: `uv add --index https://test.pypi.org/simple/ --index-strategy unsafe-best-match python-mscl` in a new uv project directory.
56+
57+
58+
## Issues:
59+
60+
If you encounter any issues, please open an issue on this repository. I would have to
61+
manually update this repository to the latest MSCL release. If it has been more than 48 hours since the latest release and I didn't update this repository, please open an issue.
62+
63+
## License
64+
65+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
66+

build_helpers/release_extractor.py

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import subprocess
55
from pathlib import Path
66

7+
MSCL_VERSION = "v67.0.0"
8+
"""The mscl version to extract."""
79

810
class ReleaseExtractor:
911
"""Will extract the .deb and .zip releases for the mscl library."""
@@ -26,24 +28,24 @@ def extract_deb(self, file: Path):
2628

2729
# Create a directory to extract the .deb file. Syntax: mscl-<arch>-<python-ver>-<mscl-ver>
2830
parts = file.stem.split("_")
29-
arch, py_ver, mscl_ver = parts[1], parts[2], parts[3]
30-
mscl_versioned_name = f"mscl-{arch}-{py_ver}-{mscl_ver}"
31+
arch, py_ver = parts[1], parts[2]
32+
mscl_versioned_name = f"mscl-{arch}-{py_ver}-{MSCL_VERSION}"
3133
mscl_versioned_dir = cwd / self.asset_dir / mscl_versioned_name
3234

3335
# If output directory exists, remove it:
3436
if mscl_versioned_dir.exists():
35-
os.system(f"rm -rf {mscl_versioned_dir}") # noqa: S605
37+
os.system(f"rm -rf {mscl_versioned_dir}")
3638

3739
mscl_versioned_dir.mkdir(parents=True, exist_ok=True)
3840
file_relative = file.absolute().relative_to(mscl_versioned_dir, walk_up=True)
3941

4042
# Extract the .deb file
41-
subprocess.run(["ar", "x", str(file_relative)], cwd=mscl_versioned_dir, check=True) # noqa: S603, S607
43+
subprocess.run(["ar", "x", str(file_relative)], cwd=mscl_versioned_dir, check=True)
4244

4345
# Extract the data.tar.gz file:
4446
data_tar = "data.tar.gz"
4547

46-
subprocess.run(["tar", "-xzf", data_tar], cwd=mscl_versioned_dir, check=True) # noqa: S603, S607
48+
subprocess.run(["tar", "-xzf", data_tar], cwd=mscl_versioned_dir, check=True)
4749

4850
found_mscl_py = list(mscl_versioned_dir.rglob("mscl.py"))
4951
found_mscl_so = list(mscl_versioned_dir.rglob("_mscl.so"))
@@ -63,9 +65,49 @@ def extract_deb(self, file: Path):
6365
if f.stem in (mscl_py.stem, mscl_so.stem):
6466
continue
6567
if f.is_dir():
66-
os.system(f"rm -rf {f}") # noqa: S605
68+
os.system(f"rm -rf {f}")
6769
else:
6870
f.unlink()
6971

7072
def extract_zip(self, file: Path) -> None:
7173
"""Extracts the .zip release."""
74+
75+
cwd = Path().cwd()
76+
77+
# Create a directory to extract the .zip file. Syntax: mscl-<arch>-<python-ver>-<mscl-ver>
78+
parts = file.stem.split("_")
79+
arch, py_ver = parts[2], parts[3]
80+
mscl_versioned_name = f"mscl-Windows-{arch}-{py_ver}-{MSCL_VERSION}"
81+
mscl_versioned_dir = cwd / self.asset_dir / mscl_versioned_name
82+
83+
# If output directory exists, remove it:
84+
if mscl_versioned_dir.exists():
85+
os.system(f"rm -rf {mscl_versioned_dir}")
86+
87+
mscl_versioned_dir.mkdir(parents=True, exist_ok=True)
88+
file_relative = file.absolute().relative_to(mscl_versioned_dir, walk_up=True)
89+
90+
# Extract the .zip file
91+
subprocess.run(["unzip", str(file_relative)], cwd=mscl_versioned_dir, check=True) # noqa: S603, S607
92+
93+
found_mscl_py = list(mscl_versioned_dir.rglob("mscl.py"))
94+
found_mscl_pyd = list(mscl_versioned_dir.rglob("_mscl.pyd"))
95+
96+
if not found_mscl_py or not found_mscl_pyd:
97+
raise FileNotFoundError(f"Could not find mscl.py or _mscl.pyd in {mscl_versioned_dir}")
98+
99+
# Move the extracted files to the root of the mscl_versioned_dir:
100+
mscl_py = found_mscl_py[0]
101+
mscl_pyd = found_mscl_pyd[0]
102+
103+
mscl_py.rename(mscl_versioned_dir / mscl_py.name)
104+
mscl_pyd.rename(mscl_versioned_dir / mscl_pyd.name)
105+
106+
# Delete the remaining files in mscl_versioned_dir:
107+
for f in mscl_versioned_dir.iterdir():
108+
if f.stem in (mscl_py.stem, mscl_pyd.stem):
109+
continue
110+
if f.is_dir():
111+
os.system(f"rm -rf {f}")
112+
else:
113+
f.unlink()

hatch_build.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
from build_helpers.release_downloader import GithubDownloader
1313

14+
MSCL_VERSION = "v67.0.0"
15+
"""The mscl version to build the wheels of."""
16+
1417

1518
class CustomBuildHook(BuildHookInterface):
1619
"""Build hook to build wheels from the extracted .deb/.zip files."""
@@ -49,42 +52,53 @@ def initialize(self, version, build_data):
4952

5053
arch = platform.machine()
5154
if arch == "x86_64":
52-
arch = "amd64"
55+
mscl_arch = "amd64"
5356
elif arch == "aarch64":
54-
arch = "arm64"
57+
mscl_arch = "arm64"
5558
elif arch == "armv7l":
56-
arch = "armhf"
59+
mscl_arch = "armhf"
60+
elif arch == "AMD64": # Windows
61+
mscl_arch = "x64"
62+
elif arch == "x86": # Windows
63+
mscl_arch = "x86"
5764
else:
58-
# TODO: Windows support
5965
raise RuntimeError(f"Unknown architecture: {arch}")
6066

61-
# c) mscl version to download:
62-
mscl_ver = "v67.0.0"
63-
64-
build_data["tag"] = f"{self._python_tag()}-{self._python_tag()}-{self._platform_tag()}"
6567
# --- STEP 2: Download the 2 mscl files (mscl.py and _mscl.so) from the git repo: ---
66-
# Folder name: mscl-<arch>-<python-ver>-<mscl-ver>
68+
# Folder name: mscl-<mscl_arch>-<python-ver>-<mscl-ver> -> Linux
69+
# Folder name: mscl-Windows-<mscl_arch>-<python-ver>-<mscl-ver> -> Windows
6770

6871
# a) Create the folder name:
69-
folder_name = f"mscl-{arch}-{py_version}-{mscl_ver}"
72+
73+
if platform.system() == "Linux":
74+
build_data["tag"] = f"{self._python_tag()}-{self._python_tag()}-{self._platform_tag()}"
75+
folder_name = f"mscl-{mscl_arch}-{py_version}-{MSCL_VERSION}"
76+
77+
# Windows is best effort matching since there's only python 3.11 available for v67.0.0:
78+
else:
79+
if mscl_arch == "x64":
80+
build_data["tag"] = "py3-none-win_amd64"
81+
elif mscl_arch == "x86":
82+
build_data["tag"] = "py3-none-win32"
83+
folder_name = f"mscl-Windows-{mscl_arch}-Python3.11-{MSCL_VERSION}"
7084

7185
# b) Use PyGithub to download the files from the folder:
7286
self.app.display_waiting(f"Downloading files for {folder_name}...")
7387

7488
gh = GithubDownloader()
7589
gh.download_assets_from_folder(
76-
tag=mscl_ver,
90+
tag=MSCL_VERSION,
7791
folder_name=f"mscl_release_assets/{folder_name}",
7892
)
7993

8094
self.app.display_success("Downloaded files successfully.")
8195
build_data["artifacts"] = ["_mscl.so", "mscl.py"]
8296

83-
# Show all files in the current directory:
84-
# self.app.display_info(f"Files in {Path().cwd()}: {list(Path().cwd().iterdir())}")
85-
8697
# --- STEP 3: Copy the files ("_mscl.so" & "mscl.py") to the src/mscl/ directory: ---
8798
# Move from root (i.e. cwd) to src/mscl
88-
subprocess.run(["mv", "mscl.py", "src/mscl/"], check=True) # noqa: S603, S607
89-
subprocess.run(["mv", "_mscl.so", "src/mscl/"], check=True) # noqa: S603, S607
90-
self.app.display_success("Moved files to src/mscl/ successfully. Building wheel...")
99+
subprocess.run(["mv", "mscl.py", "src/python_mscl/"], check=True)
100+
if platform.system() == "Windows":
101+
subprocess.run(["mv", "_mscl.pyd", "src/python_mscl/"], check=True)
102+
else:
103+
subprocess.run(["mv", "_mscl.so", "src/python_mscl/"], check=True)
104+
self.app.display_success("Moved files to src/python_mscl/ successfully. Building wheel...")

main.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
"""Entry point for scripts."""
22

3-
from build_helpers.release_downloader import GithubDownloader
4-
from build_helpers.release_extractor import ReleaseExtractor
3+
# from build_helpers.release_downloader import GithubDownloader
4+
# from build_helpers.release_extractor import ReleaseExtractor
55

66

77
def main():
8-
GithubDownloader()
8+
"""Entry point to fetch the latest release assets from the Github repository & extract them."""
9+
# gh = GithubDownloader()
910
# gh.download_release_assets("mscl_release_assets")
10-
re = ReleaseExtractor()
11-
re.extract_assets()
11+
# re = ReleaseExtractor()
12+
# re.extract_assets()
1213

1314

1415
if __name__ == "__main__":
Binary file not shown.

0 commit comments

Comments
 (0)