From 58888853b68416887fa6d1bbf0b2fcf6ca6aeb54 Mon Sep 17 00:00:00 2001 From: Ryan Hill Date: Fri, 4 Oct 2024 12:47:37 -0500 Subject: [PATCH] project setup (#1) * project setup * add typing --- .github/workflows/pre-release.yml | 38 +++++++++ .github/workflows/publish.yaml | 32 ++++++++ CHANGELOG.md | 27 +++++++ CODE_OF_CONDUCT.md | 128 ++++++++++++++++++++++++++++++ CONTRIBUTING.md | 1 + MANIFEST.in | 5 ++ README.md | 4 +- mypy.ini | 6 ++ pyproject.toml | 72 +++++++++++++++++ pyqasm/__init__.py | 51 ++++++++++++ pyqasm/exceptions.py | 22 +++++ pyqasm/py.typed | 0 pyqasm/validate.py | 45 +++++++++++ tools/stamp_pre_release.py | 25 ++++++ 14 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/pre-release.yml create mode 100644 .github/workflows/publish.yaml create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 MANIFEST.in create mode 100644 mypy.ini create mode 100644 pyproject.toml create mode 100644 pyqasm/__init__.py create mode 100644 pyqasm/exceptions.py create mode 100644 pyqasm/py.typed create mode 100644 pyqasm/validate.py create mode 100644 tools/stamp_pre_release.py diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 0000000..8dc1185 --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,38 @@ +name: Pre-release to PyPI + +on: + workflow_dispatch: + +jobs: + pypi-publish: + name: Build pre-release dist & upload to PyPI + runs-on: ubuntu-latest + environment: publish + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install build dependencies + run: python -m pip install -U pip wheel build toml-cli qbraid-core + + - name: Build binary wheel + source tarball + id: build-dev + run: | + export PRE_RELEASE_VERSION=$(python tools/stamp_pre_release.py pyqasm) + [[ "$PRE_RELEASE_VERSION" =~ .*(-a\.|-b\.|-rc\.).* ]] && echo "Deploying pre-release version '$PRE_RELEASE_VERSION'" || (echo "not pre-release version"; exit 0) + out_dir="${PWD}/dist" + tools/create_dev_build.sh $PRE_RELEASE_VERSION "${out_dir}" + echo "dir=$out_dir" >> $GITHUB_OUTPUT + + - name: Publish pre-release package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + packages-dir: ${{ steps.build-dev.outputs.dir }} \ No newline at end of file diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..a7bb736 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,32 @@ +name: Publish to PyPI + +on: + release: + types: [published] + workflow_dispatch: + +jobs: + pypi-publish: + name: Build dist & upload to PyPI + runs-on: ubuntu-latest + environment: publish + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Build binary wheel + source tarball + run: | + python3 -m pip install --upgrade pip build + python3 -m build + + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ddc0941 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +Types of changes: +- `Added`: for new features. +- `Improved`: for improvements to existing functionality. +- `Deprecated`: for soon-to-be removed features. +- `Removed`: for now removed features. +- `Fixed`: for any bug fixes. +- `Dependencies`: for updates to external libraries or packages. + +## [Unreleased] + +### Added + +### Improved / Modified + +### Deprecated + +### Removed + +### Fixed + +### Dependencies \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8073525 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +contact@qbraid.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4d218d9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +# Contributing \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..0f9728f --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include *.txt *.md +recursive-include docs *.rst *.py *.png *.ico *.txt +recursive-include pyqasm *.py +include pyqasm/py.typed +prune docs/build/ \ No newline at end of file diff --git a/README.md b/README.md index affd8f4..89e5806 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# pyqasm \ No newline at end of file +# pyqasm + +Python toolkit providing an OpenQASM 3 semantic analyzer and utilities for program analysis and compilation. \ No newline at end of file diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..42b5c51 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,6 @@ +[mypy] +ignore_missing_imports = True +incremental = True +show_error_codes = True +follow_imports = normal +cache_dir = .mypy_cache \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f5f2afb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,72 @@ +[build-system] +requires = ["setuptools>=61", "setuptools_scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "pyqasm" +version = "0.0.0" +description = "Python toolkit providing an OpenQASM 3 semantic analyzer and utilities for program analysis and compilation." +authors = [{name = "qBraid Development Team"}, {email = "contact@qbraid.com"}] +readme = "README.md" +requires-python = ">=3.10" +keywords = ["quantum", "openqasm", "symantic-analyzer", "compiler", "qbraid"] +license = {text = "GNU General Public License v3.0"} +classifiers = [ + "Development Status :: 1 - Planning", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Natural Language :: English", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + 'Topic :: Software Development', + 'Topic :: Scientific/Engineering', + "Topic :: Scientific/Engineering :: Physics", + 'Typing :: Typed', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Operating System :: MacOS', +] +dependencies = ["pyqir>=0.10.0,<0.11.0", "numpy"] + +[project.urls] +source = "https://github.com/qBraid/pyqasm" +tracker = "https://github.com/qBraid/pyqasm/issues" + +[project.optional-dependencies] +test = ["pytest", "pytest-cov", "autoqasm>=0.1.0"] +lint = ["black", "isort", "pylint", "mypy", "qbraid-cli>=0.8.5"] +docs = ["sphinx>=7.3.7,<8.1.0", "sphinx-autodoc-typehints>=1.24,<2.5", "sphinx-rtd-theme~=2.0.0", "docutils<0.22", "sphinx-copybutton"] + +[tool.setuptools_scm] +write_to = "pyqasm/_version.py" + +[tool.black] +line-length = 100 +target-version = ["py310", "py311", "py312"] + +[tool.isort] +profile = "black" +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +line_length = 100 + +[tool.pylint.'MESSAGES CONTROL'] +max-line-length = 100 +disable = "C0414,C0415,R0914,W0511" + +[tool.pylint.MASTER] +ignore-paths = [ + "^.*\\_version.py$", +] + +[tool.pytest.ini_options] +addopts = "-ra" +testpaths = ["tests"] \ No newline at end of file diff --git a/pyqasm/__init__.py b/pyqasm/__init__.py new file mode 100644 index 0000000..6e00552 --- /dev/null +++ b/pyqasm/__init__.py @@ -0,0 +1,51 @@ +# Copyright (C) 2024 qBraid +# +# This file is part of the qBraid-SDK +# +# The qBraid-SDK 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 . +# +# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3. + +""" +Top level module containing the main PyQASM functionality. + +.. currentmodule:: pyqasm + +Functions +---------- + +.. autosummary:: + :toctree: ../stubs/ + + validate + +Exceptions +----------- + +.. autosummary:: + :toctree: ../stubs/ + + PyQasmError + ValidationError + +""" +import warnings + +try: + # Injected in _version.py during the build process. + from ._version import __version__ # type: ignore +except ImportError: + warnings.warn("Importing 'pyqasm' outside a proper installation.") + __version__ = "dev" + +from .exceptions import PyQasmError, ValidationError +from .validate import validate + +__all__ = [ + "PyQasmError", + "ValidationError", + "validate", + "__version__", +] diff --git a/pyqasm/exceptions.py b/pyqasm/exceptions.py new file mode 100644 index 0000000..c444576 --- /dev/null +++ b/pyqasm/exceptions.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 qBraid +# +# This file is part of the qBraid-SDK +# +# The qBraid-SDK 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 . +# +# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3. + +""" +Module defining base PyQASM exceptions. + +""" + + +class PyQasmError(Exception): + """Base exception for all PyQASM exceptions.""" + + +class ValidationError(PyQasmError): + """Exception raised when a OpenQASM program fails validation.""" diff --git a/pyqasm/py.typed b/pyqasm/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyqasm/validate.py b/pyqasm/validate.py new file mode 100644 index 0000000..7582dae --- /dev/null +++ b/pyqasm/validate.py @@ -0,0 +1,45 @@ +# Copyright (C) 2024 qBraid +# +# This file is part of the qBraid-SDK +# +# The qBraid-SDK 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 . +# +# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3. + +""" +Module for performing semantic analysis of OpenQASM 3 programs. + +""" +from __future__ import annotations + +from typing import TYPE_CHECKING, Union + +from openqasm3.parser import QASM3ParsingError + +from .exceptions import ValidationError + +if TYPE_CHECKING: + import openqasm3.ast + + +def validate(program: Union[openqasm3.ast.Program, str]) -> None: + """Validates a given OpenQASM 3 program for semantic correctness. + + Args: + program (openqasm3.ast.Program or str): The OpenQASM 3 program to validate. + + Raises: + TypeError: If the input is not a string or an `openqasm3.ast.Program` instance. + ValidationError: If the program fails parsing or semantic validation. + """ + if isinstance(program, str): + try: + program = openqasm3.parse(program) + except QASM3ParsingError as err: + raise ValidationError(f"Failed to parse OpenQASM string: {err}") from err + elif not isinstance(program, openqasm3.ast.Program): + raise TypeError("Input quantum program must be of type 'str' or 'openqasm3.ast.Program'.") + + raise NotImplementedError("Semantic validation is not yet implemented.") diff --git a/tools/stamp_pre_release.py b/tools/stamp_pre_release.py new file mode 100644 index 0000000..7050f9b --- /dev/null +++ b/tools/stamp_pre_release.py @@ -0,0 +1,25 @@ +# Copyright (C) 2024 qBraid +# +# This file is part of the qBraid-SDK +# +# The qBraid-SDK 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 . +# +# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3. + +""" +Script for getting/bumping the next pre-release version. + +""" +import pathlib +import sys + +from qbraid_core.system.versions import get_prelease_version + +if __name__ == "__main__": + + package_name = sys.argv[1] + root = pathlib.Path(__file__).parent.parent.resolve() + version = get_prelease_version(root, package_name) + print(version)