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
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:


steps:
- name: Checkout Pyqasm
- name: Checkout PyQASM
uses: actions/checkout@v4

- name: Install Python
Expand Down Expand Up @@ -109,7 +109,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Checkout Pyqasm
- name: Checkout PyQASM
uses: actions/checkout@v4

- name: Install Python
Expand Down Expand Up @@ -138,7 +138,7 @@ jobs:
environment: release

steps:
- name: Checkout Pyqasm
- name: Checkout PyQASM
uses: actions/checkout@v4

- name: Download artifacts
Expand Down
162 changes: 162 additions & 0 deletions .github/workflows/test-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Publish to TestPyPI

on:
workflow_dispatch:

jobs:
build_wheels:
runs-on: ${{ matrix.os }}
strategy:

# Ensure that a wheel builder finishes even if another fails
fail-fast: false

matrix:
include:
# Windows 64 bit
- os: windows-latest
python: 310
platform_id: win_amd64
- os: windows-latest
python: 311
platform_id: win_amd64
- os: windows-latest
python: 312
platform_id: win_amd64
- os: windows-latest
python: 313
platform_id: win_amd64

# Linux 64 bit manylinux2014
- os: ubuntu-latest
python: 310
platform_id: manylinux_x86_64
manylinux_image: manylinux2014
- os: ubuntu-latest
python: 311
platform_id: manylinux_x86_64
manylinux_image: manylinux2014
- os: ubuntu-latest
python: 312
platform_id: manylinux_x86_64
manylinux_image: manylinux2014
- os: ubuntu-latest
python: 313
platform_id: manylinux_x86_64
manylinux_image: manylinux2014

# MacOS x86_64
- os: macos-13
python: 310
platform_id: macosx_x86_64
- os: macos-13
python: 311
platform_id: macosx_x86_64
- os: macos-13
python: 312
platform_id: macosx_x86_64
- os: macos-13
python: 313
platform_id: macosx_x86_64

# MacOS arm64
- os: macos-14
python: 310
platform_id: macosx_arm64
- os: macos-14
python: 311
platform_id: macosx_arm64
- os: macos-14
python: 312
platform_id: macosx_arm64
- os: macos-14
python: 313
platform_id: macosx_arm64


steps:
- name: Checkout PyQASM
uses: actions/checkout@v4

- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Write version file
run: python bin/write_version_file.py

- name: Build and Test Wheels
env:
CIBW_BUILD: "cp${{ matrix.python }}-${{ matrix.platform_id }}"
CIBW_ARCHS_LINUX: x86_64
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_image }}
CIBW_BEFORE_BUILD: bash {project}/bin/cibw/pre_build.sh {project}
CIBW_TEST_EXTRAS: "test,cli"
CIBW_TEST_COMMAND: bash {project}/bin/cibw/test_wheel.sh {project}
CIBW_BUILD_VERBOSITY: 1

run: bash bin/cibw/build_wheels.sh

- name: Upload built wheels
uses: actions/upload-artifact@v4
with:
name: package-wheel-cp${{ matrix.python }}-${{ matrix.os }}
path: dist/*.whl

build_sdist:
name: Source distribution
runs-on: ubuntu-latest

steps:
- name: Checkout PyQASM
uses: actions/checkout@v4

- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Write version file
run: python bin/write_version_file.py

- name: Build source distribution
run: bash bin/build_sdist.sh

- name: Test source distribution
env:
RELEASE_BUILD: "true"
run: bash bin/test_sdist.sh

- name: Store artifacts
uses: actions/upload-artifact@v4
with:
name: package-sdist
path: dist/*.tar.gz

pypi-publish:
name: Build dist & upload to PyPI
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
environment: release

steps:
- name: Checkout PyQASM
uses: actions/checkout@v4

- name: Download artifacts
uses: actions/download-artifact@v4
with:
# pattern should match the upload artifact naming convention
# of the previous jobs
pattern: package-*
path: dist
# put all files in single directory
merge-multiple: true

- name: Publish package to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
94 changes: 94 additions & 0 deletions bin/write_version_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright (C) 2025 qBraid
#
# This file is part of PyQASM
#
# PyQASM is free software released under the GNU General Public License v3
# or later. You can redistribute and/or modify it under the terms of the GPL v3.
# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
#
# THERE IS NO WARRANTY for PyQASM, as per Section 15 of the GPL v3.

"""
This script is used to write the version to the version file.
It is used to ensure that the version file is always up to date
with the version in pyproject.toml.
"""

import pathlib
import sys
import tomllib


def get_version_from_pyproject(pyproject_path: pathlib.Path) -> str:
"""Extract the version from pyproject.toml file."""
try:
with open(pyproject_path, "rb") as f:
data = tomllib.load(f)
return data["project"]["version"]
except FileNotFoundError:
print(f"Error: pyproject.toml not found at {pyproject_path}")
sys.exit(1)
except KeyError:
print("Error: Version not found in pyproject.toml")
sys.exit(1)


def parse_version_tuple(version_string: str) -> tuple[int | str, ...]:
"""Parse a semantic version string into a tuple."""
parts = version_string.split(".")

version_tuple = []
for part in parts[:3]:
try:
version_tuple.append(int(part))
except ValueError:
version_tuple.append(part)

if len(parts) > 3:
extra = ".".join(parts[3:])
if "-" in extra:
pre_release, build = extra.split("-", 1)
version_tuple.extend(pre_release.split("."))
if "+" in build:
build = build.split("+", 1)[0]
version_tuple.extend(build.split("."))
elif "+" in extra:
pre_release, build = extra.split("+", 1)
version_tuple.extend(pre_release.split("."))
else:
version_tuple.extend(extra.split("."))

return tuple(version_tuple)


def write_version_file(version_file_path: pathlib.Path, version: str) -> None:
"""Write the version to a file."""
version_file_path.parent.mkdir(parents=True, exist_ok=True)
version_tuple = parse_version_tuple(version)
content = f"""# file generated during build
# don't change, don't track in version control
TYPE_CHECKING = False
if TYPE_CHECKING:
VERSION_TUPLE = tuple[int | str, ...]
else:
VERSION_TUPLE = object

version: str
__version__: str
__version_tuple__: VERSION_TUPLE
version_tuple: VERSION_TUPLE

__version__ = version = '{version}'
__version_tuple__ = version_tuple = {version_tuple}
"""
version_file_path.write_text(content)
print(f"Version file written: {version_file_path}")


if __name__ == "__main__":
root = pathlib.Path(__file__).parent.parent.resolve()
pyproject_toml = root / "pyproject.toml"
version_file = "src" / "pyqasm" / "_version.py"

version_str = get_version_from_pyproject(pyproject_toml)
write_version_file(version_file, version_str)
10 changes: 3 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools>=74.1", "setuptools_scm", "Cython>=3.0", "numpy"]
requires = ["setuptools>=74.1", "Cython>=3.0", "numpy"]
build-backend = "setuptools.build_meta"

[project]
Expand All @@ -12,7 +12,7 @@ requires-python = ">=3.10"
keywords = ["quantum", "openqasm", "symantic-analyzer", "compiler", "qbraid"]
license = {text = "GNU General Public License v3.0"}
classifiers = [
"Development Status :: 2 - Pre-Alpha",
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"Natural Language :: English",
Expand Down Expand Up @@ -41,13 +41,9 @@ dependencies = ["numpy", "openqasm3[parser]>=1.0.0,<2.0.0"]
[project.optional-dependencies]
cli = ["typer>=0.12.1", "rich>=10.11.0", "typing-extensions"]
test = ["pytest", "pytest-cov"]
lint = ["black", "isort>=6.0.0", "pylint", "mypy", "qbraid-cli>=0.8.5"]
lint = ["black", "isort>=6.0.0", "pylint", "mypy", "qbraid-cli>=0.10.2"]
docs = ["sphinx>=7.3.7,<8.2.0", "sphinx-autodoc-typehints>=1.24,<3.1", "sphinx-rtd-theme>=2.0.0,<4.0.0", "docutils<0.22", "sphinx-copybutton"]

[tool.setuptools_scm]
version_scheme = "no-guess-dev"
write_to = "src/pyqasm/_version.py"

[tool.setuptools.package-data]
pyqasm = ["py.typed", "*.pyx"]

Expand Down
7 changes: 4 additions & 3 deletions src/pyqasm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@

"""
import warnings
from importlib.metadata import version

try:
# Injected in _version.py during the build process.
from ._version import __version__ # type: ignore
except ImportError: # pragma: no cover
warnings.warn("Importing 'pyqasm' outside a proper installation.")
__version__ = "dev"
except (ImportError, ModuleNotFoundError): # pragma: no cover
warnings.warn("Importing 'pyqasm' outside a proper installation.", UserWarning)
__version__ = version("pyqasm")

from .entrypoint import dump, dumps, load, loads
from .exceptions import PyQasmError, QasmParsingError, ValidationError
Expand Down
2 changes: 1 addition & 1 deletion src/pyqasm/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def version_callback(value: bool):
"""Show the version and exit."""
if value:
# pylint: disable-next=import-outside-toplevel
from pyqasm._version import __version__ # type: ignore
from pyqasm import __version__ # type: ignore

typer.echo(f"pyqasm/{__version__}")
raise typer.Exit(0)
Expand Down
5 changes: 5 additions & 0 deletions tests/cli/test_cli_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@
import os
import re
import shutil
import warnings

import pytest
import typer
from typer.testing import CliRunner

warnings.filterwarnings(
"ignore", "Importing 'pyqasm' outside a proper installation.", category=UserWarning
)

from pyqasm.cli.main import app
from pyqasm.cli.validate import validate_qasm

Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ commands =
[testenv:headers]
envdir = .tox/linters
skip_install = true
deps = qbraid-cli>=0.9.9
deps = qbraid-cli>=0.10.2
commands =
qbraid admin headers src tests bin examples --skip=src/pyqasm/_version.py --type=gpl -p PyQASM {posargs}

Expand Down