Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Validate distributions
on:
workflow_call:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: python -m pip install build twine
- run: python -m build
- run: python -m twine check --strict dist/*
- name: Check release tag matches package version
if: startsWith(github.ref, 'refs/tags/')
run: |
python - <<'PY'
import ast, os
from pathlib import Path
tree = ast.parse(Path('src/sysmlv2_client/__init__.py').read_text())
version = next(ast.literal_eval(node.value) for node in tree.body
if isinstance(node, ast.Assign)
and any(isinstance(t, ast.Name) and t.id == '__version__' for t in node.targets))
assert os.environ['GITHUB_REF_NAME'] == f'v{version}', 'Tag must match package version'
PY
- name: Test installed wheel outside checkout
run: |
python -m venv /tmp/wheel-test
/tmp/wheel-test/bin/python -m pip install dist/*.whl pytest requests-mock
cd /tmp
/tmp/wheel-test/bin/python -c "import sysml_api; from sysmlv2_client import SysMLV2Client"
/tmp/wheel-test/bin/python -m pytest --import-mode=importlib "$GITHUB_WORKSPACE/tests"
- name: Build wheel from source distribution
run: python -m pip wheel --no-deps dist/*.tar.gz --wheel-dir /tmp/sdist-wheels
- uses: actions/upload-artifact@v4
with:
name: distributions
path: dist/*
38 changes: 38 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Publish package
on:
release:
types: [published]
workflow_dispatch:
permissions:
contents: read
jobs:
build:
uses: ./.github/workflows/package.yml
testpypi:
if: github.event_name == 'workflow_dispatch'
needs: build
runs-on: ubuntu-latest
environment: testpypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: distributions
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
pypi:
if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags/v')
needs: build
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: distributions
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
61 changes: 21 additions & 40 deletions .github/workflows/test-sysmlv2-python-client.yml
Original file line number Diff line number Diff line change
@@ -1,47 +1,28 @@
name: Test sysmlv2-python-client

name: Test sysml-api-client
on:
push:
branches: [ main, develop ]
branches: [main, develop]
pull_request:
branches: [ main, develop ]

branches: [main, develop]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
id: setup-python
uses: actions/setup-python@v4
with:
python-version: '3.12'

- name: Cache pip dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install pytest pytest-cov requests-mock

- name: Run tests with coverage
run: |
mkdir -p test-results
pytest -v --tb=short --junitxml=test-results/results.xml --cov=src/sysmlv2_client --cov-report=xml:test-results/coverage.xml --cov-report=html:test-results/htmlcov

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-sysmlv2-python-client
path: test-results/
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: python -m pip install -e ".[test]"
- run: python -m pytest -v --tb=short --junitxml=test-results/results.xml --cov=sysmlv2_client --cov-report=xml:test-results/coverage.xml
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.python-version }}
path: test-results/
package:
uses: ./.github/workflows/package.yml
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ __pycache__/
# Virtual Environments
venv/
.venv/
.venv-wheel/
env/

# Jupyter Notebook Checkpoints
Expand All @@ -23,7 +24,8 @@ env/
# Flexo Setup Sensitive Files
flexo-setup/docker-compose/env/*.env
/build
/src/sysmlv2_python_client.egg-info
/dist/
*.egg-info/
/test-results
coverage.xml
/.venv-wheel
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Changelog

## 0.1.0

- Initial PyPI packaging as `sysml-api-client`.
- Includes the existing `sysmlv2_client` client, exceptions, and `sysml_api` helpers.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include CHANGELOG.md RELEASING.md
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# SysML v2 Python Client
# sysml-api-client

A basic Python client library for interacting with a SysML v2 API server, specifically tested against the OpenMBEE Flexo implementation.
A Python client for the [OMG Systems Modeling API and Services](https://www.omg.org/spec/SystemsModelingAPI), specifically tested against the OpenMBEE Flexo implementation. The PyPI name is `sysml-api-client`; Python imports remain `sysmlv2_client` and `sysml_api`.

## Installation

Requires Python 3.10 or newer. Once published on PyPI:

```bash
python -m pip install sysml-api-client
```

```python
from sysmlv2_client import SysMLV2Client
```

See [RELEASING.md](RELEASING.md) for build validation and publisher setup.

## Features

Expand All @@ -20,13 +34,8 @@ Follow instructions [here](https://github.com/Open-MBEE/flexo-mms-sysmlv2.git)

### 2. Install Client (Development)

```python
# Example: Add src to path if running scripts/notebooks from project root
import sys
import os
sys.path.insert(0, os.path.abspath('./src'))

from sysmlv2_client import SysMLV2Client
```bash
python -m pip install -e ".[test]"
```

## Basic Usage
Expand Down Expand Up @@ -95,15 +104,14 @@ except Exception as e:

```

See the [examples/basic_usage.ipynb](examples/basic_usage.ipynb) Jupyter Notebook for more detailed examples covering commits, branches, tags, and element retrieval/modification via commits.

## Running Tests

Unit tests are implemented using `pytest` and `requests-mock`.

1. **Install Dependencies:**
```bash
pip install pytest requests requests-mock
python -m pip install -e ".[test]"
```
2. **Run Tests:** Navigate to the project root directory in your terminal and run:
```bash
Expand Down
68 changes: 68 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Releasing sysml-api-client

The distribution includes `sysmlv2_client` and `sysml_api`. The version comes
from `src/sysmlv2_client/__init__.py` (initial release: `0.1.0`).

## One-time setup

Register a pending Trusted Publisher from your PyPI organization's **Publishing**
page to create the project under organization ownership on the first upload.
If you manually create the project first, add a normal Trusted Publisher to
that project instead.

| Publisher field | PyPI | TestPyPI |
| --- | --- | --- |
| Project name | `sysml-api-client` | `sysml-api-client` |
| GitHub owner | `Open-MBEE` | `Open-MBEE` |
| Repository | `sysmlv2-python-client` | `sysmlv2-python-client` |
| Workflow filename | `publish.yml` | `publish.yml` |
| Environment | `pypi` | `testpypi` |

TestPyPI needs its own account and publisher configuration. Create GitHub
environments `pypi` and `testpypi`; configure required reviewers for `pypi`
and restrict it to release tags as appropriate. No PyPI token secret is needed.
Check that PyPI accepts the project name; a pending publisher does not reserve it.

References: [organization pending publishers](https://blog.pypi.org/posts/2025-11-10-trusted-publishers-coming-to-orgs/),
[existing project publishers](https://docs.pypi.org/trusted-publishers/adding-a-publisher/).

## Local validation

Create and activate a virtual environment, then run:

```bash
python -m pip install -e ".[test]" build twine
python -m pytest
python -m build
python -m twine check --strict dist/*
```

Start with an empty `dist/` directory. CI also tests the installed wheel outside
the checkout, imports both packages, and builds a wheel from the source archive.

## Release

1. Update `__version__` and `CHANGELOG.md`; each upload needs a new version.
2. Push the changes and wait for the test matrix and package checks to pass.
3. Run **Publish package** manually from the desired commit to upload to TestPyPI.
For repeated rehearsals use new versions, such as `0.1.0rc1`, `0.1.0rc2`.
4. In a clean virtual environment, verify the candidate (substitute its version):

```bash
python -m pip install requests
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps sysml-api-client==0.1.0
python -c "import sysml_api; from sysmlv2_client import SysMLV2Client"
```

5. Publish a GitHub release tagged `v0.1.0` at the tested commit, substituting
the actual version. This triggers production publishing. Approve the `pypi`
environment deployment if configured. The workflow rejects mismatched tags.
6. Verify production installation in a fresh environment:

```bash
python -m pip install sysml-api-client==0.1.0
python -c "import sysml_api; from sysmlv2_client import SysMLV2Client"
```

Published files cannot be replaced on PyPI or TestPyPI. Increment the version
to correct a release.
36 changes: 30 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
[project]
name = "sysmlv2-python-client"
version = "0.1.0"
description = "Python client for SysML v2 services"
authors = [{name = "Your Name", email = "you@example.com"}]
dependencies = []
name = "sysml-api-client"
dynamic = ["version"]
description = "Python client for the OMG Systems Modeling API and Services"
readme = "README.md"
requires-python = ">=3.10"
license = "Apache-2.0"
license-files = ["LICENSE"]
dependencies = ["requests>=2.28,<3"]
classifiers = [
"Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]

[project.urls]
Repository = "https://github.com/Open-MBEE/sysmlv2-python-client"
Issues = "https://github.com/Open-MBEE/sysmlv2-python-client/issues"
Specification = "https://www.omg.org/spec/SystemsModelingAPI"

[project.optional-dependencies]
test = ["pytest>=7", "pytest-cov>=4", "requests-mock>=1.11"]

[tool.setuptools.packages.find]
where = ["src"]
include = ["sysmlv2_client*", "sysml_api*"]
namespaces = false

[tool.setuptools.dynamic]
version = {attr = "sysmlv2_client.__version__"}

[build-system]
requires = ["setuptools>=61.0"]
requires = ["setuptools>=77.0.3"]
build-backend = "setuptools.build_meta"
1 change: 0 additions & 1 deletion src/sysml_api/api_lib.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import numpy as np
from sysmlv2_client import SysMLV2Client, SysMLV2Error, SysMLV2NotFoundError
from typing import Any, Dict

Expand Down
2 changes: 1 addition & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import requests_mock
import requests

from src.sysmlv2_client import (
from sysmlv2_client import (
SysMLV2Client,
SysMLV2Error,
SysMLV2AuthError,
Expand Down
Loading