From 8c715672456d84f4062c9d1583c4d91598f15102 Mon Sep 17 00:00:00 2001 From: Mitch D'Ewart Date: Tue, 20 Jan 2026 14:32:48 -0800 Subject: [PATCH 1/5] Update openpulse dependency to >=1.0.0 and fix type annotations openpulse 1.0.0 introduces stricter AST type definitions. This commit updates the codebase to be compatible with the new types: - Add type assertions where AST types can be None (e.g., ClassicalType) - Add casts for nested block bodies (ForInLoop, WhileLoop, etc.) which require list[Statement] but ProgramState.body is list[Statement | Pragma] - Use ast.FrameType() instance instead of class in OQFunctionCall - Add explicit type annotations to avoid inference issues - Cast RangeDefinition to Expression where needed ProgramState.body now stores both Statement and Pragma types together to preserve their relative ordering per OpenQASM 3.0 spec ("pragmas should be processed as soon as they are encountered"). Pragmas remain enforced as global-only via Program.pragma(). Also updates openqasm3 dependency to >=1.0.0 for compatibility. --- oqpy/base.py | 21 ++++-- oqpy/classical_types.py | 16 +++- oqpy/control_flow.py | 33 +++++--- oqpy/program.py | 113 ++++++++++++++++++---------- oqpy/pulse.py | 2 +- oqpy/quantum_types.py | 14 ++-- oqpy/subroutines.py | 8 +- oqpy/timing.py | 7 +- poetry.lock | 159 ++++++++++++++++++++++++++++++++------- pyproject.toml | 4 +- tests/test_directives.py | 2 +- 11 files changed, 272 insertions(+), 107 deletions(-) diff --git a/oqpy/base.py b/oqpy/base.py index 8f4df63..b2b1152 100644 --- a/oqpy/base.py +++ b/oqpy/base.py @@ -252,6 +252,7 @@ def compute_product_types(left: AstConvertible, right: AstConvertible) -> ast.Cl raise TypeError(f"Could not identify types for product {left} and {right}") from e if isinstance(result_type, Exception): raise result_type + assert result_type is not None return result_type @@ -306,6 +307,7 @@ def compute_quotient_types(left: AstConvertible, right: AstConvertible) -> ast.C raise TypeError(f"Could not identify types for quotient {left} and {right}") from e if isinstance(result_type, Exception): raise result_type + assert result_type is not None return result_type @@ -529,10 +531,13 @@ def to_ast(program: Program, item: AstConvertible) -> ast.Expression: return detect_and_convert_constants(item, program) return ast.FloatLiteral(item) if isinstance(item, slice): - return ast.RangeDefinition( - to_ast(program, item.start) if item.start is not None else None, - to_ast(program, item.stop - 1) if item.stop is not None else None, - to_ast(program, item.step) if item.step is not None else None, + return cast( + ast.Expression, + ast.RangeDefinition( + to_ast(program, item.start) if item.start is not None else None, + to_ast(program, item.stop - 1) if item.stop is not None else None, + to_ast(program, item.step) if item.step is not None else None, + ), ) if isinstance(item, Iterable): return ast.ArrayLiteral([to_ast(program, i) for i in item]) @@ -570,15 +575,15 @@ def make_annotations(vals: Sequence[str | tuple[str, str]]) -> list[ast.Annotati def detect_and_convert_constants(val: float | np.floating[Any], program: Program) -> ast.Expression: """Construct a float ast expression which is either a literal or an expression using constants.""" if val == 0: - return ast.FloatLiteral(val) + return ast.FloatLiteral(float(val)) if val < 0.5 or val > 100: - return ast.FloatLiteral(val) + return ast.FloatLiteral(float(val)) if math.isnan(val): - return ast.FloatLiteral(val) + return ast.FloatLiteral(float(val)) x = val / (math.pi / 4.0) rx = round(x) if not math.isclose(x, rx, rel_tol=1e-12): - return ast.FloatLiteral(val) + return ast.FloatLiteral(float(val)) term: OQPyExpression if rx == 4: term = classical_types.pi diff --git a/oqpy/classical_types.py b/oqpy/classical_types.py index f161b15..005a63a 100644 --- a/oqpy/classical_types.py +++ b/oqpy/classical_types.py @@ -31,6 +31,7 @@ Type, TypeVar, Union, + cast, ) from openpulse import ast @@ -150,7 +151,7 @@ def arrayreference_( dims: int | list[int], ) -> ast.ArrayReferenceType: """Create an array reference type.""" - dim = ( + dim: ast.Expression | list[ast.Expression] = ( ast.IntegerLiteral(dims) if isinstance(dims, int) else [ast.IntegerLiteral(d) for d in dims] ) return ast.ArrayReferenceType(base_type=dtype, dimensions=dim) @@ -226,6 +227,8 @@ def to_ast(self, program: Program) -> ast.Identifier: def make_declaration_statement(self, program: Program) -> ast.Statement: """Make an ast statement that declares the OQpy variable.""" + assert self.type is not None + stmt: ast.IODeclaration | ast.ClassicalDeclaration if isinstance(self.init_expression, str) and self.init_expression in ("input", "output"): stmt = ast.IODeclaration( ast.IOKeyword[self.init_expression], self.type, self.to_ast(program) @@ -408,9 +411,12 @@ def __init__( # Creating a dummy variable supports IntVar[64] etc. base_type_instance = base_type() if isinstance(base_type_instance, _SizedVar): - array_base_type = base_type_instance.type_cls( - size=ast.IntegerLiteral(base_type_instance.size) + size_arg = ( + ast.IntegerLiteral(base_type_instance.size) + if base_type_instance.size is not None + else None ) + array_base_type = base_type_instance.type_cls(size=size_arg) # type: ignore[call-arg] elif isinstance(base_type_instance, ComplexVar): array_base_type = base_type_instance.type_cls(base_type=base_type_instance.base_type) else: @@ -437,7 +443,9 @@ def __init__( ) def __getitem__(self, index: AstConvertible) -> OQIndexExpression: - return OQIndexExpression(collection=self, index=index, type_=self.base_type().type_cls()) + base_instance = self.base_type() + assert base_instance.type is not None + return OQIndexExpression(collection=self, index=index, type_=base_instance.type) class OQIndexExpression(OQPyExpression): diff --git a/oqpy/control_flow.py b/oqpy/control_flow.py index 69162bc..7f085ed 100644 --- a/oqpy/control_flow.py +++ b/oqpy/control_flow.py @@ -18,7 +18,7 @@ from __future__ import annotations import contextlib -from typing import TYPE_CHECKING, Iterable, Iterator, Optional, TypeVar, overload +from typing import TYPE_CHECKING, Iterable, Iterator, Optional, TypeVar, cast, overload from openpulse import ast @@ -55,7 +55,7 @@ def If(program: Program, condition: OQPyExpression) -> Iterator[None]: program._push() yield state = program._pop() - program._state.add_if_clause(to_ast(program, condition), state.body) + program._state.add_if_clause(to_ast(program, condition), state.statements_as_block()) @contextlib.contextmanager @@ -74,7 +74,7 @@ def Else(program: Program) -> Iterator[None]: program._push() yield state = program._pop() - program._state.add_else_clause(state.body) + program._state.add_else_clause(state.statements_as_block()) # Overloads needed due mypy bug, see @@ -118,6 +118,7 @@ def ForIn( yield var state = program._pop() + set_declaration: ast.RangeDefinition | ast.DiscreteSet | ast.Expression if isinstance(iterator, range): # A range can only be iterated over integers. assert identifier_type is IntVar, "A range can only be looped over an integer." @@ -131,7 +132,10 @@ def ForIn( set_declaration = to_ast(program, iterator) stmt = ast.ForInLoop( - identifier_type.type_cls(), var.to_ast(program), set_declaration, state.body + identifier_type.type_cls(), + var.to_ast(program), + set_declaration, + state.statements_as_block(), ) program._add_statement(stmt) @@ -150,14 +154,17 @@ def __init__(self, start: AstConvertible, stop: AstConvertible, step: AstConvert def to_ast(self, program: Program) -> ast.Expression: """Convert to an ast.RangeDefinition.""" - return ast.RangeDefinition( - to_ast(program, self.start), - ast.BinaryExpression( - lhs=to_ast(program, self.stop), - op=ast.BinaryOperator["-"], - rhs=ast.IntegerLiteral(value=1), + return cast( + ast.Expression, + ast.RangeDefinition( + to_ast(program, self.start), + ast.BinaryExpression( + lhs=to_ast(program, self.stop), + op=ast.BinaryOperator["-"], + rhs=ast.IntegerLiteral(value=1), + ), + to_ast(program, self.step) if self.step != 1 else None, ), - to_ast(program, self.step) if self.step != 1 else None, ) @@ -175,4 +182,6 @@ def While(program: Program, condition: OQPyExpression) -> Iterator[None]: program._push() yield state = program._pop() - program._add_statement(ast.WhileLoop(to_ast(program, condition), state.body)) + program._add_statement( + ast.WhileLoop(to_ast(program, condition), state.statements_as_block()) + ) diff --git a/oqpy/program.py b/oqpy/program.py index 54c6536..fcc713a 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -25,7 +25,7 @@ import warnings from copy import deepcopy -from typing import Any, Hashable, Iterable, Iterator, Optional +from typing import Any, Hashable, Iterable, Iterator, Optional, cast from openpulse import ast from openpulse.printer import dumps @@ -53,6 +53,11 @@ class ProgramState: A new ProgramState is created every time a context (such as the control flow constructs If/Else/ForIn/While) is created. A program will retain a stack of ProgramState objects for all currently open contexts. + + The body stores both Statements and Pragmas to preserve their relative + ordering. Per OpenQASM 3.0 spec, pragmas should be "processed as soon as + they are encountered". Pragmas should only be added at global scope (not + inside control flow blocks). See openqasm/openqasm#468. """ def __init__(self) -> None: @@ -78,15 +83,30 @@ def finalize_if_clause(self) -> None: self.add_statement(if_clause) def add_statement(self, stmt: ast.Statement | ast.Pragma) -> None: - # This function accepts Statement and Pragma even though - # it seems to conflict with the definition of ast.Program. - # Issue raised in https://github.com/openqasm/openqasm/issues/468 - assert isinstance(stmt, (ast.Statement, ast.Pragma)) - if isinstance(stmt, ast.Statement) and self.annotations: - stmt.annotations = self.annotations + list(stmt.annotations) - self.annotations = [] - self.finalize_if_clause() - self.body.append(stmt) + """Add a statement or pragma to this program state. + + Both statements and pragmas are stored in body to preserve their + relative ordering. Pragmas should only be added at global scope. + """ + if isinstance(stmt, ast.Pragma): + self.finalize_if_clause() + self.body.append(stmt) + else: + if self.annotations: + stmt.annotations = self.annotations + list(stmt.annotations) + self.annotations = [] + self.finalize_if_clause() + self.body.append(stmt) + + def statements_as_block(self) -> list[ast.Statement]: + """Return body as list[Statement] for use in nested blocks. + + This cast is safe because pragmas are enforced as global-only via + Program.pragma(), which raises RuntimeError if called inside a + control flow context. Nested blocks (ForInLoop, WhileLoop, etc.) + therefore never contain Pragma nodes. + """ + return cast(list[ast.Statement], self.body) class Program: @@ -221,7 +241,7 @@ def autodeclare(self, encal: bool = False) -> None: encal=encal, ) - def _add_statement(self, stmt: ast.Statement) -> None: + def _add_statement(self, stmt: ast.Statement | ast.Pragma) -> None: """Add a statment to the current context's program state.""" self._state.add_statement(stmt) @@ -260,14 +280,15 @@ def _add_defcal( """ self.defcals[(tuple(qubit_names), name, tuple(arguments))] = stmt - def _make_externs_statements(self, auto_encal: bool = False) -> list[ast.ExternDeclaration]: + def _make_externs_statements(self, auto_encal: bool = False) -> list[ast.Statement]: """Return a list of extern statements for inclusion at beginning of program. if auto_encal is True, any externs using openpulse types will be wrapped in a Cal block. """ if not auto_encal: return list(self.externs.values()) - openqasm_externs, openpulse_externs = [], [] + openqasm_externs: list[ast.Statement] = [] + openpulse_externs: list[ast.Statement | ast.Pragma] = [] openpulse_types = ast.PortType, ast.FrameType, ast.WaveformType for extern_statement in self.externs.values(): for arg in extern_statement.arguments: @@ -316,20 +337,26 @@ def to_ast( warnings.warn( f"Annotation(s) {mutating_prog._state.annotations} not applied to any statement" ) - statements = [] + statements: list[ast.Statement | ast.Pragma] = [] if include_externs: statements += mutating_prog._make_externs_statements(encal_declarations) statements += ( - [ - mutating_prog.subroutines[subroutine_name] - for subroutine_name in mutating_prog.subroutines - if subroutine_name not in mutating_prog.declared_subroutines - ] - + [ - mutating_prog.gates[gate_name] - for gate_name in mutating_prog.gates - if gate_name not in mutating_prog.declared_gates - ] + cast( + list[ast.Statement | ast.Pragma], + [ + mutating_prog.subroutines[subroutine_name] + for subroutine_name in mutating_prog.subroutines + if subroutine_name not in mutating_prog.declared_subroutines + ], + ) + + cast( + list[ast.Statement | ast.Pragma], + [ + mutating_prog.gates[gate_name] + for gate_name in mutating_prog.gates + if gate_name not in mutating_prog.declared_gates + ], + ) + mutating_prog._state.body ) if encal: @@ -425,7 +452,12 @@ def delay( qubits_or_frames = [qubits_or_frames] ast_duration = to_ast(self, convert_float_to_duration(time, require_nonnegative=True)) ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) - self._add_statement(ast.DelayInstruction(ast_duration, ast_qubits_or_frames)) + self._add_statement( + ast.DelayInstruction( + ast_duration, + cast(list[ast.IndexedIdentifier | ast.Identifier], ast_qubits_or_frames), + ) + ) return self def barrier(self, qubits_or_frames: Iterable[AstConvertible]) -> Program: @@ -438,7 +470,7 @@ def function_call( self, name: str, args: Iterable[AstConvertible], - assigns_to: AstConvertible = None, + assigns_to: AstConvertible | None = None, ) -> None: """Add a function call with an optional output assignment.""" function_call_node = ast.FunctionCall(ast.Identifier(name), map_to_ast(self, args)) @@ -597,14 +629,16 @@ def gate( modifiers, ast.Identifier(name), map_to_ast(self, args), - map_to_ast(self, used_qubits), + cast(list[ast.IndexedIdentifier | ast.Identifier], map_to_ast(self, used_qubits)), ) ) return self def reset(self, qubit: quantum_types.Qubit) -> Program: """Reset a particular qubit.""" - self._add_statement(ast.QuantumReset(qubits=qubit.to_ast(self))) + qubit_ast = qubit.to_ast(self) + assert isinstance(qubit_ast, (ast.IndexedIdentifier, ast.Identifier)) + self._add_statement(ast.QuantumReset(qubits=qubit_ast)) return self def measure( @@ -614,10 +648,11 @@ def measure( If provided, store the result in the given output location. """ + target_ast = optional_ast(self, output_location) self._add_statement( ast.QuantumMeasurementStatement( measure=ast.QuantumMeasurement(ast.Identifier(qubit.name)), - target=optional_ast(self, output_location), + target=cast(ast.IndexedIdentifier | ast.Identifier | None, target_ast), ) ) return self @@ -641,13 +676,13 @@ def _do_assignment(self, var: AstConvertible, op: str, value: AstConvertible) -> """Helper function for variable assignment operations.""" if isinstance(var, classical_types.DurationVar): value = convert_float_to_duration(value) - var_ast = to_ast(self, var) + var_ast: ast.Identifier | ast.IndexedIdentifier | ast.Expression = to_ast(self, var) if isinstance(var_ast, ast.IndexExpression): assert isinstance(var_ast.collection, ast.Identifier) var_ast = ast.IndexedIdentifier(name=var_ast.collection, indices=[var_ast.index]) self._add_statement( ast.ClassicalAssignment( - var_ast, + cast(ast.Identifier | ast.IndexedIdentifier, var_ast), ast.AssignmentOperator[op], to_ast(self, value), ) @@ -697,16 +732,16 @@ def visit_Program(self, node: ast.Program, context: None = None) -> None: self.generic_visit(node, context) def visit_ForInLoop(self, node: ast.ForInLoop, context: None = None) -> None: - node.block = self.process_statement_list(node.block) + node.block = cast(list[ast.Statement], self.process_statement_list(node.block)) self.generic_visit(node, context) def visit_WhileLoop(self, node: ast.WhileLoop, context: None = None) -> None: - node.block = self.process_statement_list(node.block) + node.block = cast(list[ast.Statement], self.process_statement_list(node.block)) self.generic_visit(node, context) def visit_BranchingStatement(self, node: ast.BranchingStatement, context: None = None) -> None: - node.if_block = self.process_statement_list(node.if_block) - node.else_block = self.process_statement_list(node.else_block) + node.if_block = cast(list[ast.Statement], self.process_statement_list(node.if_block)) + node.else_block = cast(list[ast.Statement], self.process_statement_list(node.else_block)) self.generic_visit(node, context) def visit_CalibrationStatement( @@ -718,14 +753,14 @@ def visit_CalibrationStatement( def visit_SubroutineDefinition( self, node: ast.SubroutineDefinition, context: None = None ) -> None: - node.body = self.process_statement_list(node.body) + node.body = cast(list[ast.Statement], self.process_statement_list(node.body)) self.generic_visit(node, context) def process_statement_list( - self, statements: list[ast.Statement | ast.Pragma] + self, statements: list[ast.Statement | ast.Pragma] | list[ast.Statement] ) -> list[ast.Statement | ast.Pragma]: - new_list = [] - cal_stmts = [] + new_list: list[ast.Statement | ast.Pragma] = [] + cal_stmts: list[ast.Statement | ast.Pragma] = [] for stmt in statements: if isinstance(stmt, ast.CalibrationStatement): cal_stmts.extend(stmt.body) diff --git a/oqpy/pulse.py b/oqpy/pulse.py index d960836..14dcda5 100644 --- a/oqpy/pulse.py +++ b/oqpy/pulse.py @@ -76,7 +76,7 @@ def __init__( else: assert frequency is not None init_expression = OQFunctionCall( - "newframe", {"port": port, "frequency": frequency, "phase": phase}, ast.FrameType + "newframe", {"port": port, "frequency": frequency, "phase": phase}, ast.FrameType() ) super().__init__( init_expression, name, needs_declaration=needs_declaration, annotations=annotations diff --git a/oqpy/quantum_types.py b/oqpy/quantum_types.py index 7173d96..feb6dca 100644 --- a/oqpy/quantum_types.py +++ b/oqpy/quantum_types.py @@ -18,7 +18,7 @@ from __future__ import annotations import contextlib -from typing import TYPE_CHECKING, Iterator, Optional, Sequence, Union +from typing import TYPE_CHECKING, Iterator, Optional, Sequence, Union, cast from openpulse import ast from openpulse.printer import dumps @@ -99,8 +99,10 @@ def __init__(self, collection: Qubit, index: AstConvertible): def to_ast(self, program: Program) -> ast.IndexedIdentifier: """Converts this indexed qubit array into an ast node.""" + collection_ast = to_ast(program, self.collection) + assert isinstance(collection_ast, ast.Identifier) return ast.IndexedIdentifier( - name=to_ast(program, self.collection), indices=[[to_ast(program, self.index)]] + name=collection_ast, indices=[[to_ast(program, self.index)]] ) @@ -143,11 +145,12 @@ def gate( yield None state = program._pop() + # Cast is safe: gate bodies only contain quantum statements (no classical or pragmas) stmt = ast.QuantumGateDefinition( name=ast.Identifier(name), arguments=arguments_ast, qubits=[ast.Identifier(q.name) for q in qubits], - body=state.body, + body=cast(list[ast.QuantumStatement], state.body), ) if declare_here: program._add_statement(stmt) @@ -173,11 +176,12 @@ def defcal( qubits = [qubits] assert return_type is None or isinstance(return_type, ast.ClassicalType) - arguments_ast = [] + arguments_ast: list[ast.ClassicalArgument | ast.Expression] = [] variables = [] if arguments is not None: for arg in arguments: if isinstance(arg, _ClassicalVar): + assert arg.type is not None arguments_ast.append( ast.ClassicalArgument(type=arg.type, name=ast.Identifier(name=arg.name)) ) @@ -200,7 +204,7 @@ def defcal( arguments_ast, [ast.Identifier(q.name) for q in qubits], return_type, - state.body, + state.statements_as_block(), ) program._add_statement(stmt) program._add_defcal( diff --git a/oqpy/subroutines.py b/oqpy/subroutines.py index 375cbe5..7802bb1 100644 --- a/oqpy/subroutines.py +++ b/oqpy/subroutines.py @@ -110,7 +110,7 @@ def increment_variable(int[32] i) { argnames = list(inspect.signature(func).parameters.keys()) type_hints = get_type_hints(func) inputs = {} # used as inputs when calling the actual python function - arguments = [] # used in the ast definition of the subroutine + arguments: list[ast.ClassicalArgument | ast.QuantumArgument] = [] for argname in argnames[1:]: # arg 0 should be program if argname not in type_hints: raise ValueError(f"No type hint provided for {argname} on subroutine {name}.") @@ -131,6 +131,7 @@ def increment_variable(int[32] i) { input_ = inputs[argname] = type_hints[argname](name=argname) if isinstance(input_, _ClassicalVar): + assert input_.type is not None arguments.append(ast.ClassicalArgument(input_.type, ast.Identifier(argname))) elif isinstance(input_, Qubit): arguments.append(ast.QuantumArgument(ast.Identifier(input_.name), None)) @@ -141,10 +142,9 @@ def increment_variable(int[32] i) { output = func(inner_prog, **inputs) inner_prog.autodeclare() inner_prog._state.finalize_if_clause() - body = inner_prog._state.body if isinstance(output, OQPyExpression): return_type = output.type - body.append(ast.ReturnStatement(to_ast(inner_prog, output))) + inner_prog._state.body.append(ast.ReturnStatement(to_ast(inner_prog, output))) elif output is None: return_type = None if type_hints.get("return", False): @@ -161,7 +161,7 @@ def increment_variable(int[32] i) { identifier, arguments=arguments, return_type=return_type, - body=body, + body=inner_prog._state.statements_as_block(), ) stmt.annotations = make_annotations(annotations) diff --git a/oqpy/timing.py b/oqpy/timing.py index ac80d65..c10d81c 100644 --- a/oqpy/timing.py +++ b/oqpy/timing.py @@ -19,7 +19,7 @@ import contextlib import warnings -from typing import TYPE_CHECKING, Iterator, cast +from typing import TYPE_CHECKING, Iterator, cast, List from openpulse import ast @@ -47,7 +47,10 @@ def Box(program: Program, duration: AstConvertible | None = None) -> Iterator[No program._push() yield state = program._pop() - program._add_statement(ast.Box(optional_ast(program, duration), state.body)) + # Cast is safe: Box bodies only contain quantum statements (no classical or pragmas) + program._add_statement( + ast.Box(optional_ast(program, duration), cast(List[ast.QuantumStatement], state.body)) + ) def make_duration(time: AstConvertible) -> HasToAst: diff --git a/poetry.lock b/poetry.lock index de7695f..c737f0e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "alabaster" @@ -6,6 +6,7 @@ version = "0.7.12" description = "A configurable sidebar-enabled Sphinx theme" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, @@ -17,6 +18,7 @@ version = "4.11.1" description = "ANTLR 4.11.1 runtime for Python 3" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "antlr4-python3-runtime-4.11.1.tar.gz", hash = "sha256:a53de701312f9bdacc5258a6872cd6c62b90d3a90ae25e494026f76267333b60"}, {file = "antlr4_python3_runtime-4.11.1-py3-none-any.whl", hash = "sha256:ff1954eda1ca9072c02bf500387d0c86cb549bef4dbb3b64f39468b547ec5f6b"}, @@ -28,6 +30,7 @@ version = "2.12.13" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.7.2" +groups = ["dev"] files = [ {file = "astroid-2.12.13-py3-none-any.whl", hash = "sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907"}, {file = "astroid-2.12.13.tar.gz", hash = "sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7"}, @@ -47,16 +50,17 @@ version = "21.4.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["dev"] files = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, ] [package.extras] -dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] -tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] +tests-no-zope = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] [[package]] name = "Babel" @@ -64,6 +68,7 @@ version = "2.10.3" description = "Internationalization utilities" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, @@ -78,6 +83,7 @@ version = "24.3.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, @@ -114,7 +120,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.7.4) ; sys_platform != \"win32\" or implementation_name != \"pypy\"", "aiohttp (>=3.7.4,!=3.9.0) ; sys_platform == \"win32\" and implementation_name == \"pypy\""] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -124,6 +130,7 @@ version = "5.0.1" description = "An easy safelist-based HTML-sanitizing tool." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, @@ -135,7 +142,7 @@ webencodings = "*" [package.extras] css = ["tinycss2 (>=1.1.0,<1.2)"] -dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"] +dev = ["Sphinx (==4.3.2)", "black (==22.3.0) ; implementation_name == \"cpython\"", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961) ; implementation_name == \"cpython\"", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"] [[package]] name = "certifi" @@ -143,6 +150,7 @@ version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, @@ -154,6 +162,7 @@ version = "2.0.0" description = "Command line program to validate and convert CITATION.cff files." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "cffconvert-2.0.0-py3-none-any.whl", hash = "sha256:573c825e4e16173d99396dc956bd22ff5d4f84215cc16b6ab05299124f5373bb"}, {file = "cffconvert-2.0.0.tar.gz", hash = "sha256:b4379ee415c6637dc9e3e7ba196605cb3cedcea24613e4ea242c607d9e98eb50"}, @@ -177,6 +186,8 @@ version = "1.15.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = "*" +groups = ["dev"] +markers = "sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, @@ -253,6 +264,7 @@ version = "2.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.6.0" +groups = ["dev"] files = [ {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"}, @@ -267,6 +279,7 @@ version = "8.1.3" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, @@ -281,6 +294,8 @@ version = "0.4.5" description = "Cross-platform colored terminal text." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["dev"] +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, @@ -292,6 +307,7 @@ version = "0.9.1" description = "Python parser for the CommonMark Markdown spec" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, @@ -306,6 +322,7 @@ version = "6.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"}, {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"}, @@ -363,7 +380,7 @@ files = [ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "cryptography" @@ -371,6 +388,8 @@ version = "42.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "sys_platform == \"linux\"" files = [ {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, @@ -425,6 +444,7 @@ version = "0.3.5.1" description = "serialize all of python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +groups = ["dev"] files = [ {file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"}, {file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"}, @@ -439,6 +459,7 @@ version = "0.6.2" description = "Pythonic argument parser, that will make you smile" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] @@ -449,6 +470,7 @@ version = "0.17.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["dev"] files = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, @@ -460,6 +482,8 @@ version = "1.1.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version <= \"3.10\"" files = [ {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, @@ -474,6 +498,7 @@ version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, @@ -485,6 +510,7 @@ version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -496,10 +522,12 @@ version = "4.12.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, ] +markers = {main = "python_version < \"3.10\""} [package.dependencies] zipp = ">=0.5" @@ -507,7 +535,7 @@ zipp = ">=0.5" [package.extras] docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" @@ -515,6 +543,7 @@ version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, @@ -526,6 +555,7 @@ version = "5.10.1" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.6.1,<4.0" +groups = ["dev"] files = [ {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, @@ -543,6 +573,8 @@ version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "sys_platform == \"linux\"" files = [ {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, @@ -550,7 +582,7 @@ files = [ [package.extras] test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] -trio = ["async_generator", "trio"] +trio = ["async_generator ; python_version == \"3.6\"", "trio"] [[package]] name = "jinja2" @@ -558,6 +590,7 @@ version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, @@ -575,6 +608,7 @@ version = "3.2.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, @@ -596,6 +630,7 @@ version = "23.7.0" description = "Store and access your passwords safely." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "keyring-23.7.0-py3-none-any.whl", hash = "sha256:e67fc91a7955785fd2efcbccdd72d7dacf136dbc381d27de305b2b660b3de886"}, {file = "keyring-23.7.0.tar.gz", hash = "sha256:782e1cd1132e91bf459fcd243bcf25b326015c1ac0b198e4408f91fa6791062b"}, @@ -609,7 +644,7 @@ SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""] [[package]] name = "lazy-object-proxy" @@ -617,6 +652,7 @@ version = "1.7.1" description = "A fast and thorough lazy object proxy." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, @@ -663,6 +699,7 @@ version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, @@ -687,6 +724,7 @@ version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, @@ -736,6 +774,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -747,6 +786,7 @@ version = "0.3.1" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mdit-py-plugins-0.3.1.tar.gz", hash = "sha256:3fc13298497d6e04fe96efdd41281bfe7622152f9caa1815ea99b5c893de9441"}, {file = "mdit_py_plugins-0.3.1-py3-none-any.whl", hash = "sha256:606a7f29cf56dbdfaf914acb21709b8f8ee29d857e8f29dcc33d8cb84c57bfa1"}, @@ -766,6 +806,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -777,6 +818,7 @@ version = "1.4.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, @@ -823,6 +865,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["main", "dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -834,6 +877,7 @@ version = "0.18.1" description = "An extended commonmark compliant parser, with bridges to docutils & sphinx." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "myst-parser-0.18.1.tar.gz", hash = "sha256:79317f4bb2c13053dd6e64f9da1ba1da6cd9c40c8a430c447a7b146a594c246d"}, {file = "myst_parser-0.18.1-py3-none-any.whl", hash = "sha256:61b275b85d9f58aa327f370913ae1bec26ebad372cc99f3ab85c8ec3ee8d9fb8"}, @@ -860,6 +904,7 @@ version = "1.23.4" description = "NumPy is the fundamental package for array computing with Python." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "numpy-1.23.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:95d79ada05005f6f4f337d3bb9de8a7774f259341c70bc88047a1f7b96a4bcb2"}, {file = "numpy-1.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:926db372bc4ac1edf81cfb6c59e2a881606b409ddc0d0920b988174b2e2a767f"}, @@ -893,19 +938,20 @@ files = [ [[package]] name = "openpulse" -version = "0.5.0" +version = "1.0.1" description = "Reference OpenPulse AST in Python" optional = false python-versions = "*" +groups = ["main"] files = [ - {file = "openpulse-0.5.0-py3-none-any.whl", hash = "sha256:c91b69633366381f3fdbc0c9be8c37c114b2d8e469f667ff9b0f78632e00c395"}, - {file = "openpulse-0.5.0.tar.gz", hash = "sha256:d7b1d940c0e081975f5ebdad2b378d24e4a612b73fce1bc2e26948d907f5db1c"}, + {file = "openpulse-1.0.1-py3-none-any.whl", hash = "sha256:75fb2d4d7f74db3a86027719744541fcb725e1f5b79e14b78dc5b34ed8c66e87"}, + {file = "openpulse-1.0.1.tar.gz", hash = "sha256:4c184e3012907ec35f04202ed72621037b1a06d70195769576bfc9e62c01bf94"}, ] [package.dependencies] -antlr4-python3-runtime = ">=4.7,<4.12" +antlr4-python3-runtime = ">=4.7,<4.14" importlib-metadata = {version = "*", markers = "python_version < \"3.10\""} -openqasm3 = {version = ">=0.5.0", extras = ["parser"]} +openqasm3 = {version = ">=1.0.0,<2.0", extras = ["parser"]} [package.extras] all = ["pytest (>=6.0)", "pyyaml"] @@ -917,6 +963,7 @@ version = "0.1.1" description = "Pygments tools for OpenQASM" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "openqasm-pygments-0.1.1.tar.gz", hash = "sha256:31f70d540c44ca3ff00f0806e1e8dbfd23ab1d060bc0e125328994a7e0168067"}, {file = "openqasm_pygments-0.1.1-py3-none-any.whl", hash = "sha256:7a65b3d8e87bb54e525fc8cd973b83b5bc45efeea64ba84cd611cace0277901e"}, @@ -927,22 +974,23 @@ Pygments = ">=2.0,<3.0" [[package]] name = "openqasm3" -version = "0.5.0" +version = "1.0.1" description = "Reference OpenQASM AST in Python" optional = false python-versions = "*" +groups = ["main"] files = [ - {file = "openqasm3-0.5.0-py3-none-any.whl", hash = "sha256:40991ac057b9e3c208d1b34242b0aad8a3b9840df0335a652b1e4e4248937b1c"}, - {file = "openqasm3-0.5.0.tar.gz", hash = "sha256:bf8bf4ed098393447e552eaea18b0a34a2429d228477683d6b579348bc17bfc8"}, + {file = "openqasm3-1.0.1-py3-none-any.whl", hash = "sha256:0d3a1ebe3465e3ea619bcaa369858bba8944cbb0c49604b24f94662d3ec41d41"}, + {file = "openqasm3-1.0.1.tar.gz", hash = "sha256:c589dc05d4ced50ca24167d14e0f2c916e717499ba0442e0ff2a3030ef312d0a"}, ] [package.dependencies] -antlr4-python3-runtime = {version = ">=4.7,<4.14", optional = true, markers = "extra == \"parser\""} -importlib-metadata = {version = "*", optional = true, markers = "python_version < \"3.10\" and extra == \"parser\""} +antlr4_python3_runtime = {version = ">=4.7,<4.14", optional = true, markers = "extra == \"parser\""} +importlib_metadata = {version = "*", optional = true, markers = "python_version < \"3.10\" and extra == \"parser\""} [package.extras] -all = ["antlr4-python3-runtime (>=4.7,<4.14)", "importlib-metadata", "pytest (>=6.0)", "pyyaml"] -parser = ["antlr4-python3-runtime (>=4.7,<4.14)", "importlib-metadata"] +all = ["antlr4_python3_runtime (>=4.7,<4.14)", "importlib_metadata ; python_version < \"3.10\"", "pytest (>=6.0)", "pyyaml"] +parser = ["antlr4_python3_runtime (>=4.7,<4.14)", "importlib_metadata ; python_version < \"3.10\""] tests = ["pytest (>=6.0)", "pyyaml"] [[package]] @@ -951,6 +999,7 @@ version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, @@ -962,6 +1011,7 @@ version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +groups = ["dev"] files = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, @@ -973,6 +1023,7 @@ version = "1.8.3" description = "Query metadatdata from sdists / bdists / installed packages." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +groups = ["dev"] files = [ {file = "pkginfo-1.8.3-py2.py3-none-any.whl", hash = "sha256:848865108ec99d4901b2f7e84058b6e7660aae8ae10164e015a6dcf5b242a594"}, {file = "pkginfo-1.8.3.tar.gz", hash = "sha256:a84da4318dd86f870a9447a8c98340aa06216bfc6f2b7bdc4b8766984ae1867c"}, @@ -987,6 +1038,7 @@ version = "2.5.2" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, @@ -1002,6 +1054,7 @@ version = "1.0.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, @@ -1017,6 +1070,7 @@ version = "8.0.0" description = "Get CPU info with pure Python 2 & 3" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "py-cpuinfo-8.0.0.tar.gz", hash = "sha256:5f269be0e08e33fd959de96b34cd4aeeeacac014dd8305f70eb28d06de2345c5"}, ] @@ -1027,6 +1081,8 @@ version = "2.21" description = "C parser in Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +markers = "sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, @@ -1038,6 +1094,7 @@ version = "6.1.1" description = "Python docstring style checker" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, @@ -1055,13 +1112,14 @@ version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, ] [package.extras] -plugins = ["importlib-metadata"] +plugins = ["importlib-metadata ; python_version < \"3.8\""] [[package]] name = "pykwalify" @@ -1069,6 +1127,7 @@ version = "1.8.0" description = "Python lib/cli for JSON/YAML schema validation" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "pykwalify-1.8.0-py2.py3-none-any.whl", hash = "sha256:731dfa87338cca9f559d1fca2bdea37299116e3139b73f78ca90a543722d6651"}, {file = "pykwalify-1.8.0.tar.gz", hash = "sha256:796b2ad3ed4cb99b88308b533fb2f559c30fa6efb4fa9fda11347f483d245884"}, @@ -1085,6 +1144,7 @@ version = "2.15.6" description = "python code static checker" optional = false python-versions = ">=3.7.2" +groups = ["dev"] files = [ {file = "pylint-2.15.6-py3-none-any.whl", hash = "sha256:15060cc22ed6830a4049cf40bc24977744df2e554d38da1b2657591de5bcd052"}, {file = "pylint-2.15.6.tar.gz", hash = "sha256:25b13ddcf5af7d112cf96935e21806c1da60e676f952efb650130f2a4483421c"}, @@ -1111,6 +1171,7 @@ version = "0.19.2" description = "Persistent/Functional/Immutable data structures" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pyrsistent-0.19.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d6982b5a0237e1b7d876b60265564648a69b14017f3b5f908c5be2de3f9abb7a"}, {file = "pyrsistent-0.19.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:187d5730b0507d9285a96fca9716310d572e5464cadd19f22b63a6976254d77a"}, @@ -1142,6 +1203,7 @@ version = "7.4.0" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, @@ -1164,6 +1226,7 @@ version = "4.0.0" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"}, {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"}, @@ -1184,6 +1247,7 @@ version = "4.1.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, @@ -1202,6 +1266,7 @@ version = "2.8.2" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["dev"] files = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, @@ -1216,6 +1281,7 @@ version = "2022.4" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "pytz-2022.4-py2.py3-none-any.whl", hash = "sha256:2c0784747071402c6e99f0bafdb7da0fa22645f06554c7ae06bf6358897e9c91"}, {file = "pytz-2022.4.tar.gz", hash = "sha256:48ce799d83b6f8aab2020e369b627446696619e79645419610b9facd909b3174"}, @@ -1227,6 +1293,8 @@ version = "0.2.0" description = "" optional = false python-versions = "*" +groups = ["dev"] +markers = "sys_platform == \"win32\"" files = [ {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, @@ -1238,6 +1306,7 @@ version = "6.0" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, @@ -1287,6 +1356,7 @@ version = "35.0" description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "readme_renderer-35.0-py3-none-any.whl", hash = "sha256:73b84905d091c31f36e50b4ae05ae2acead661f6a09a9abb4df7d2ddcdb6a698"}, {file = "readme_renderer-35.0.tar.gz", hash = "sha256:a727999acfc222fc21d82a12ed48c957c4989785e5865807c65a487d21677497"}, @@ -1306,6 +1376,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1327,6 +1398,7 @@ version = "0.9.1" description = "A utility belt for advanced users of python-requests" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, @@ -1341,6 +1413,7 @@ version = "2.0.0" description = "Validating URI References per RFC 3986" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, @@ -1355,6 +1428,7 @@ version = "12.5.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.6.3,<4.0.0" +groups = ["dev"] files = [ {file = "rich-12.5.1-py3-none-any.whl", hash = "sha256:2eb4e6894cde1e017976d2975ac210ef515d7548bc595ba20e195fb9628acdeb"}, {file = "rich-12.5.1.tar.gz", hash = "sha256:63a5c5ce3673d3d5fbbf23cd87e11ab84b6b451436f1b7f19ec54b6bc36ed7ca"}, @@ -1374,6 +1448,7 @@ version = "0.17.21" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3" +groups = ["dev"] files = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, @@ -1392,6 +1467,8 @@ version = "0.2.7" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" optional = false python-versions = ">=3.5" +groups = ["dev"] +markers = "python_version <= \"3.10\" and platform_python_implementation == \"CPython\"" files = [ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, @@ -1438,6 +1515,8 @@ version = "3.3.2" description = "Python bindings to FreeDesktop.org Secret Service API" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "sys_platform == \"linux\"" files = [ {file = "SecretStorage-3.3.2-py3-none-any.whl", hash = "sha256:755dc845b6ad76dcbcbc07ea3da75ae54bb1ea529eb72d15f83d26499a5df319"}, {file = "SecretStorage-3.3.2.tar.gz", hash = "sha256:0a8eb9645b320881c222e827c26f4cfcf55363e8b374a021981ef886657a912f"}, @@ -1453,6 +1532,7 @@ version = "65.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "setuptools-65.5.1-py3-none-any.whl", hash = "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31"}, {file = "setuptools-65.5.1.tar.gz", hash = "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f"}, @@ -1460,7 +1540,7 @@ files = [ [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov ; platform_python_implementation != \"PyPy\"", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -1469,6 +1549,7 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1480,6 +1561,7 @@ version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, @@ -1491,6 +1573,7 @@ version = "5.3.0" description = "Python documentation generator" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, @@ -1518,7 +1601,7 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] +test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast ; python_version < \"3.8\""] [[package]] name = "sphinx-rtd-theme" @@ -1526,6 +1609,7 @@ version = "1.0.0" description = "Read the Docs theme for Sphinx" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +groups = ["dev"] files = [ {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, @@ -1544,6 +1628,7 @@ version = "1.0.2" description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, @@ -1559,6 +1644,7 @@ version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, @@ -1574,6 +1660,7 @@ version = "2.0.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, @@ -1589,6 +1676,7 @@ version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -1603,6 +1691,7 @@ version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, @@ -1618,6 +1707,7 @@ version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, @@ -1633,6 +1723,7 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -1644,6 +1735,8 @@ version = "1.2.3" description = "A lil' TOML parser" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "python_version <= \"3.10\"" files = [ {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, @@ -1655,6 +1748,7 @@ version = "0.11.6" description = "Style preserving TOML library" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, @@ -1666,6 +1760,7 @@ version = "4.0.1" description = "Collection of utilities for publishing packages on PyPI" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "twine-4.0.1-py3-none-any.whl", hash = "sha256:42026c18e394eac3e06693ee52010baa5313e4811d5a11050e7d48436cf41b9e"}, {file = "twine-4.0.1.tar.gz", hash = "sha256:96b1cf12f7ae611a4a40b6ae8e9570215daff0611828f5fe1f37a16255ab24a0"}, @@ -1688,6 +1783,7 @@ version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, @@ -1699,13 +1795,14 @@ version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1716,6 +1813,7 @@ version = "0.5.1" description = "Character encoding aliases for legacy web content" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, @@ -1727,6 +1825,7 @@ version = "1.14.1" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +groups = ["dev"] files = [ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, @@ -1810,16 +1909,18 @@ version = "3.8.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, ] +markers = {main = "python_version < \"3.10\""} [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = ">=3.8,<4.0" -content-hash = "6f83048a984aecc5f411089449a5698420891a0a298f9d16c62f20b03ac51fd9" +content-hash = "01dfcc9444df27307b4995963a164320fd7ffb4bd1a42e3fa1a94be3ffc7e574" diff --git a/pyproject.toml b/pyproject.toml index 3c7a1dc..e700179 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,8 +16,8 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.8,<4.0" -# 0.4 loosens the antlr4-python3-runtime constraints -openpulse = ">=0.5.0" +# 1.0 contains SwitchStatement and CompoundStatement AST nodes +openpulse = ">=1.0.0" numpy = [ {version = ">=1.17.3", python = ">=3.8,<3.9"}, {version = ">=1.19.3", python = ">=3.9,<3.10"}, diff --git a/tests/test_directives.py b/tests/test_directives.py index 3d3f717..f2b4e5f 100644 --- a/tests/test_directives.py +++ b/tests/test_directives.py @@ -943,7 +943,7 @@ def multiply(int[32] x, int[32] y) -> int[32] { def delay50ns(qubit q) { delay[50.0ns] q; } - def get(array[int[32], 3] arr, int[32] i) -> int { + def get(array[int[32], 3] arr, int[32] i) -> int[32] { return arr[i]; } int[32] y = 2; From 86437dfbebe5e5e5f62c70b8c1946b8c6006a74d Mon Sep 17 00:00:00 2001 From: Mitch D'Ewart Date: Tue, 20 Jan 2026 14:49:22 -0800 Subject: [PATCH 2/5] Fix style --- oqpy/classical_types.py | 1 - oqpy/control_flow.py | 4 +--- oqpy/quantum_types.py | 4 +--- oqpy/timing.py | 2 +- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/oqpy/classical_types.py b/oqpy/classical_types.py index 005a63a..c8d16fb 100644 --- a/oqpy/classical_types.py +++ b/oqpy/classical_types.py @@ -31,7 +31,6 @@ Type, TypeVar, Union, - cast, ) from openpulse import ast diff --git a/oqpy/control_flow.py b/oqpy/control_flow.py index 7f085ed..4b261cd 100644 --- a/oqpy/control_flow.py +++ b/oqpy/control_flow.py @@ -182,6 +182,4 @@ def While(program: Program, condition: OQPyExpression) -> Iterator[None]: program._push() yield state = program._pop() - program._add_statement( - ast.WhileLoop(to_ast(program, condition), state.statements_as_block()) - ) + program._add_statement(ast.WhileLoop(to_ast(program, condition), state.statements_as_block())) diff --git a/oqpy/quantum_types.py b/oqpy/quantum_types.py index feb6dca..36120ad 100644 --- a/oqpy/quantum_types.py +++ b/oqpy/quantum_types.py @@ -101,9 +101,7 @@ def to_ast(self, program: Program) -> ast.IndexedIdentifier: """Converts this indexed qubit array into an ast node.""" collection_ast = to_ast(program, self.collection) assert isinstance(collection_ast, ast.Identifier) - return ast.IndexedIdentifier( - name=collection_ast, indices=[[to_ast(program, self.index)]] - ) + return ast.IndexedIdentifier(name=collection_ast, indices=[[to_ast(program, self.index)]]) @contextlib.contextmanager diff --git a/oqpy/timing.py b/oqpy/timing.py index c10d81c..661deee 100644 --- a/oqpy/timing.py +++ b/oqpy/timing.py @@ -19,7 +19,7 @@ import contextlib import warnings -from typing import TYPE_CHECKING, Iterator, cast, List +from typing import TYPE_CHECKING, Iterator, List, cast from openpulse import ast From 0720eaa6d6aed95f889052579db6ffa353db0cae Mon Sep 17 00:00:00 2001 From: Mitch D'Ewart Date: Tue, 20 Jan 2026 15:02:04 -0800 Subject: [PATCH 3/5] Update for python 3.9 checks --- oqpy/classical_types.py | 7 +------ oqpy/program.py | 17 ++++++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/oqpy/classical_types.py b/oqpy/classical_types.py index c8d16fb..3eb0551 100644 --- a/oqpy/classical_types.py +++ b/oqpy/classical_types.py @@ -20,7 +20,6 @@ import functools import random import string -import sys from typing import ( TYPE_CHECKING, Any, @@ -51,10 +50,6 @@ from oqpy.program import Program - if sys.version_info < (3, 10): - EllipsisType = type(Ellipsis) - else: - from types import EllipsisType __all__ = [ "pi", @@ -255,7 +250,7 @@ def __class_getitem__(cls: Type[_SizedVarT], item: int | None) -> Callable[..., # Allows IntVar[64]() notation return functools.partial(cls, size=item) - def __init__(self, *args: Any, size: int | None | EllipsisType = ..., **kwargs: Any): + def __init__(self, *args: Any, size: Any = ..., **kwargs: Any): if size is ...: self.size = self.default_size elif size is None: diff --git a/oqpy/program.py b/oqpy/program.py index fcc713a..28ec78b 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -25,7 +25,7 @@ import warnings from copy import deepcopy -from typing import Any, Hashable, Iterable, Iterator, Optional, cast +from typing import Any, Hashable, Iterable, Iterator, List, Optional, Union, cast from openpulse import ast from openpulse.printer import dumps @@ -342,7 +342,7 @@ def to_ast( statements += mutating_prog._make_externs_statements(encal_declarations) statements += ( cast( - list[ast.Statement | ast.Pragma], + List[Union[ast.Statement, ast.Pragma]], [ mutating_prog.subroutines[subroutine_name] for subroutine_name in mutating_prog.subroutines @@ -350,7 +350,7 @@ def to_ast( ], ) + cast( - list[ast.Statement | ast.Pragma], + List[Union[ast.Statement, ast.Pragma]], [ mutating_prog.gates[gate_name] for gate_name in mutating_prog.gates @@ -455,7 +455,7 @@ def delay( self._add_statement( ast.DelayInstruction( ast_duration, - cast(list[ast.IndexedIdentifier | ast.Identifier], ast_qubits_or_frames), + cast(List[Union[ast.IndexedIdentifier, ast.Identifier]], ast_qubits_or_frames), ) ) return self @@ -629,7 +629,10 @@ def gate( modifiers, ast.Identifier(name), map_to_ast(self, args), - cast(list[ast.IndexedIdentifier | ast.Identifier], map_to_ast(self, used_qubits)), + cast( + List[Union[ast.IndexedIdentifier, ast.Identifier]], + map_to_ast(self, used_qubits), + ), ) ) return self @@ -652,7 +655,7 @@ def measure( self._add_statement( ast.QuantumMeasurementStatement( measure=ast.QuantumMeasurement(ast.Identifier(qubit.name)), - target=cast(ast.IndexedIdentifier | ast.Identifier | None, target_ast), + target=cast(Union[ast.IndexedIdentifier, ast.Identifier, None], target_ast), ) ) return self @@ -682,7 +685,7 @@ def _do_assignment(self, var: AstConvertible, op: str, value: AstConvertible) -> var_ast = ast.IndexedIdentifier(name=var_ast.collection, indices=[var_ast.index]) self._add_statement( ast.ClassicalAssignment( - cast(ast.Identifier | ast.IndexedIdentifier, var_ast), + cast(Union[ast.Identifier, ast.IndexedIdentifier], var_ast), ast.AssignmentOperator[op], to_ast(self, value), ) From 1547bfae0b59f4d8367cb0b39e71da671b0e5186 Mon Sep 17 00:00:00 2001 From: Mitch D'Ewart Date: Tue, 20 Jan 2026 15:08:14 -0800 Subject: [PATCH 4/5] Fix for python 3.8 --- oqpy/program.py | 12 +++--- oqpy/quantum_types.py | 4 +- poetry.lock | 93 ++++++++++++++++++++++++------------------- 3 files changed, 60 insertions(+), 49 deletions(-) diff --git a/oqpy/program.py b/oqpy/program.py index 28ec78b..87e4cc7 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -106,7 +106,7 @@ def statements_as_block(self) -> list[ast.Statement]: control flow context. Nested blocks (ForInLoop, WhileLoop, etc.) therefore never contain Pragma nodes. """ - return cast(list[ast.Statement], self.body) + return cast(List[ast.Statement], self.body) class Program: @@ -735,16 +735,16 @@ def visit_Program(self, node: ast.Program, context: None = None) -> None: self.generic_visit(node, context) def visit_ForInLoop(self, node: ast.ForInLoop, context: None = None) -> None: - node.block = cast(list[ast.Statement], self.process_statement_list(node.block)) + node.block = cast(List[ast.Statement], self.process_statement_list(node.block)) self.generic_visit(node, context) def visit_WhileLoop(self, node: ast.WhileLoop, context: None = None) -> None: - node.block = cast(list[ast.Statement], self.process_statement_list(node.block)) + node.block = cast(List[ast.Statement], self.process_statement_list(node.block)) self.generic_visit(node, context) def visit_BranchingStatement(self, node: ast.BranchingStatement, context: None = None) -> None: - node.if_block = cast(list[ast.Statement], self.process_statement_list(node.if_block)) - node.else_block = cast(list[ast.Statement], self.process_statement_list(node.else_block)) + node.if_block = cast(List[ast.Statement], self.process_statement_list(node.if_block)) + node.else_block = cast(List[ast.Statement], self.process_statement_list(node.else_block)) self.generic_visit(node, context) def visit_CalibrationStatement( @@ -756,7 +756,7 @@ def visit_CalibrationStatement( def visit_SubroutineDefinition( self, node: ast.SubroutineDefinition, context: None = None ) -> None: - node.body = cast(list[ast.Statement], self.process_statement_list(node.body)) + node.body = cast(List[ast.Statement], self.process_statement_list(node.body)) self.generic_visit(node, context) def process_statement_list( diff --git a/oqpy/quantum_types.py b/oqpy/quantum_types.py index 36120ad..be9d76a 100644 --- a/oqpy/quantum_types.py +++ b/oqpy/quantum_types.py @@ -18,7 +18,7 @@ from __future__ import annotations import contextlib -from typing import TYPE_CHECKING, Iterator, Optional, Sequence, Union, cast +from typing import TYPE_CHECKING, Iterator, List, Optional, Sequence, Union, cast from openpulse import ast from openpulse.printer import dumps @@ -148,7 +148,7 @@ def gate( name=ast.Identifier(name), arguments=arguments_ast, qubits=[ast.Identifier(q.name) for q in qubits], - body=cast(list[ast.QuantumStatement], state.body), + body=cast(List[ast.QuantumStatement], state.body), ) if declare_here: program._add_statement(stmt) diff --git a/poetry.lock b/poetry.lock index c737f0e..dae5f3f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1302,52 +1302,63 @@ files = [ [[package]] name = "PyYAML" -version = "6.0" +version = "6.0.1" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" groups = ["dev"] files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] From 46ac259b94286c1eed3cff3b1f2803c5985b46c5 Mon Sep 17 00:00:00 2001 From: Mitch D'Ewart Date: Wed, 21 Jan 2026 08:53:35 -0800 Subject: [PATCH 5/5] Address PR review feedback: simplify type annotations and add comments - Simplify ProgramState.body to list[ast.Statement] (matching openqasm ast typing) - Remove statements_as_block() method, use .body directly - Simplify add_statement() back to original form - Remove unnecessary casts in to_ast() method - Add explanatory comments for RangeDefinition casts in base.py and control_flow.py - Add map_to_identifiers() helper with runtime type verification - Fix Box comment to accurately describe the cast requirement --- oqpy/base.py | 21 +++++++++++ oqpy/control_flow.py | 11 +++--- oqpy/program.py | 81 +++++++++++++------------------------------ oqpy/quantum_types.py | 4 +-- oqpy/subroutines.py | 2 +- oqpy/timing.py | 4 ++- 6 files changed, 59 insertions(+), 64 deletions(-) diff --git a/oqpy/base.py b/oqpy/base.py index b2b1152..ebfeb3f 100644 --- a/oqpy/base.py +++ b/oqpy/base.py @@ -531,6 +531,9 @@ def to_ast(program: Program, item: AstConvertible) -> ast.Expression: return detect_and_convert_constants(item, program) return ast.FloatLiteral(item) if isinstance(item, slice): + # Technically, RangeDefinition is not an Expression + # but we can treat it like one, since an expression is allowable anywhere + # a RangeDefinition is allowed. return cast( ast.Expression, ast.RangeDefinition( @@ -560,6 +563,24 @@ def map_to_ast(program: Program, items: Iterable[AstConvertible]) -> list[ast.Ex return [to_ast(program, item) for item in items] +def map_to_identifiers( + program: Program, items: Iterable[AstConvertible] +) -> list[ast.IndexedIdentifier | ast.Identifier]: + """Convert a sequence of items into identifier ast nodes. + + Verifies at runtime that each item converts to an Identifier or IndexedIdentifier. + """ + result: list[ast.IndexedIdentifier | ast.Identifier] = [] + for item in items: + node = to_ast(program, item) + if not isinstance(node, (ast.Identifier, ast.IndexedIdentifier)): + raise TypeError( + f"Expected Identifier or IndexedIdentifier, got {type(node).__name__}" + ) + result.append(node) + return result + + def make_annotations(vals: Sequence[str | tuple[str, str]]) -> list[ast.Annotation]: """Convert strings/tuples of strings into Annotation ast nodes.""" anns: list[ast.Annotation] = [] diff --git a/oqpy/control_flow.py b/oqpy/control_flow.py index 4b261cd..f56834b 100644 --- a/oqpy/control_flow.py +++ b/oqpy/control_flow.py @@ -55,7 +55,7 @@ def If(program: Program, condition: OQPyExpression) -> Iterator[None]: program._push() yield state = program._pop() - program._state.add_if_clause(to_ast(program, condition), state.statements_as_block()) + program._state.add_if_clause(to_ast(program, condition), state.body) @contextlib.contextmanager @@ -74,7 +74,7 @@ def Else(program: Program) -> Iterator[None]: program._push() yield state = program._pop() - program._state.add_else_clause(state.statements_as_block()) + program._state.add_else_clause(state.body) # Overloads needed due mypy bug, see @@ -135,7 +135,7 @@ def ForIn( identifier_type.type_cls(), var.to_ast(program), set_declaration, - state.statements_as_block(), + state.body, ) program._add_statement(stmt) @@ -154,6 +154,9 @@ def __init__(self, start: AstConvertible, stop: AstConvertible, step: AstConvert def to_ast(self, program: Program) -> ast.Expression: """Convert to an ast.RangeDefinition.""" + # Technically, RangeDefinition is not an Expression + # but we can treat it like one, since an expression is allowable anywhere + # a RangeDefinition is allowed. return cast( ast.Expression, ast.RangeDefinition( @@ -182,4 +185,4 @@ def While(program: Program, condition: OQPyExpression) -> Iterator[None]: program._push() yield state = program._pop() - program._add_statement(ast.WhileLoop(to_ast(program, condition), state.statements_as_block())) + program._add_statement(ast.WhileLoop(to_ast(program, condition), state.body)) diff --git a/oqpy/program.py b/oqpy/program.py index 87e4cc7..34d8403 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -38,6 +38,7 @@ Var, expr_matches, map_to_ast, + map_to_identifiers, optional_ast, to_ast, ) @@ -53,15 +54,12 @@ class ProgramState: A new ProgramState is created every time a context (such as the control flow constructs If/Else/ForIn/While) is created. A program will retain a stack of ProgramState objects for all currently open contexts. - - The body stores both Statements and Pragmas to preserve their relative - ordering. Per OpenQASM 3.0 spec, pragmas should be "processed as soon as - they are encountered". Pragmas should only be added at global scope (not - inside control flow blocks). See openqasm/openqasm#468. """ def __init__(self) -> None: - self.body: list[ast.Statement | ast.Pragma] = [] + # Typed as list[ast.Statement] even though it may contain Pragmas. + # This matches the openqasm ast typing. See openqasm/openqasm#468. + self.body: list[ast.Statement] = [] self.if_clause: Optional[ast.BranchingStatement] = None self.annotations: list[ast.Annotation] = [] @@ -83,30 +81,12 @@ def finalize_if_clause(self) -> None: self.add_statement(if_clause) def add_statement(self, stmt: ast.Statement | ast.Pragma) -> None: - """Add a statement or pragma to this program state. - - Both statements and pragmas are stored in body to preserve their - relative ordering. Pragmas should only be added at global scope. - """ - if isinstance(stmt, ast.Pragma): - self.finalize_if_clause() - self.body.append(stmt) - else: - if self.annotations: - stmt.annotations = self.annotations + list(stmt.annotations) - self.annotations = [] - self.finalize_if_clause() - self.body.append(stmt) - - def statements_as_block(self) -> list[ast.Statement]: - """Return body as list[Statement] for use in nested blocks. - - This cast is safe because pragmas are enforced as global-only via - Program.pragma(), which raises RuntimeError if called inside a - control flow context. Nested blocks (ForInLoop, WhileLoop, etc.) - therefore never contain Pragma nodes. - """ - return cast(List[ast.Statement], self.body) + """Add a statement or pragma to this program state.""" + if isinstance(stmt, ast.Statement) and self.annotations: + stmt.annotations = self.annotations + list(stmt.annotations) + self.annotations = [] + self.finalize_if_clause() + self.body.append(stmt) # type: ignore[arg-type] class Program: @@ -337,33 +317,27 @@ def to_ast( warnings.warn( f"Annotation(s) {mutating_prog._state.annotations} not applied to any statement" ) - statements: list[ast.Statement | ast.Pragma] = [] + statements: list[ast.Statement] = [] if include_externs: statements += mutating_prog._make_externs_statements(encal_declarations) statements += ( - cast( - List[Union[ast.Statement, ast.Pragma]], - [ - mutating_prog.subroutines[subroutine_name] - for subroutine_name in mutating_prog.subroutines - if subroutine_name not in mutating_prog.declared_subroutines - ], - ) - + cast( - List[Union[ast.Statement, ast.Pragma]], - [ - mutating_prog.gates[gate_name] - for gate_name in mutating_prog.gates - if gate_name not in mutating_prog.declared_gates - ], - ) + [ + mutating_prog.subroutines[subroutine_name] + for subroutine_name in mutating_prog.subroutines + if subroutine_name not in mutating_prog.declared_subroutines + ] + + [ + mutating_prog.gates[gate_name] + for gate_name in mutating_prog.gates + if gate_name not in mutating_prog.declared_gates + ] + mutating_prog._state.body ) if encal: - statements = [ast.CalibrationStatement(statements)] + statements = [ast.CalibrationStatement(statements)] # type: ignore[arg-type] if encal_declarations: statements = [ast.CalibrationGrammarDeclaration("openpulse")] + statements - prog = ast.Program(statements=statements, version=mutating_prog.version) + prog = ast.Program(statements=statements, version=mutating_prog.version) # type: ignore[arg-type] if encal_declarations: MergeCalStatementsPass().visit(prog) return prog @@ -451,13 +425,8 @@ def delay( if not isinstance(qubits_or_frames, Iterable): qubits_or_frames = [qubits_or_frames] ast_duration = to_ast(self, convert_float_to_duration(time, require_nonnegative=True)) - ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) - self._add_statement( - ast.DelayInstruction( - ast_duration, - cast(List[Union[ast.IndexedIdentifier, ast.Identifier]], ast_qubits_or_frames), - ) - ) + ast_qubits_or_frames = map_to_identifiers(self, qubits_or_frames) + self._add_statement(ast.DelayInstruction(ast_duration, ast_qubits_or_frames)) return self def barrier(self, qubits_or_frames: Iterable[AstConvertible]) -> Program: diff --git a/oqpy/quantum_types.py b/oqpy/quantum_types.py index be9d76a..488dd9e 100644 --- a/oqpy/quantum_types.py +++ b/oqpy/quantum_types.py @@ -202,7 +202,7 @@ def defcal( arguments_ast, [ast.Identifier(q.name) for q in qubits], return_type, - state.statements_as_block(), + state.body, ) program._add_statement(stmt) program._add_defcal( @@ -216,4 +216,4 @@ def Cal(program: Program) -> Iterator[None]: program._push() yield state = program._pop() - program._add_statement(ast.CalibrationStatement(state.body)) + program._add_statement(ast.CalibrationStatement(state.body)) # type: ignore[arg-type] diff --git a/oqpy/subroutines.py b/oqpy/subroutines.py index 7802bb1..df05d30 100644 --- a/oqpy/subroutines.py +++ b/oqpy/subroutines.py @@ -161,7 +161,7 @@ def increment_variable(int[32] i) { identifier, arguments=arguments, return_type=return_type, - body=inner_prog._state.statements_as_block(), + body=inner_prog._state.body, ) stmt.annotations = make_annotations(annotations) diff --git a/oqpy/timing.py b/oqpy/timing.py index 661deee..0d1a4ca 100644 --- a/oqpy/timing.py +++ b/oqpy/timing.py @@ -47,7 +47,9 @@ def Box(program: Program, duration: AstConvertible | None = None) -> Iterator[No program._push() yield state = program._pop() - # Cast is safe: Box bodies only contain quantum statements (no classical or pragmas) + # Cast is needed because ast.Box expects list[QuantumStatement]. + # OpenQASM spec requires Box to contain only quantum statements, but this + # is not enforced at runtime by oqpy. program._add_statement( ast.Box(optional_ast(program, duration), cast(List[ast.QuantumStatement], state.body)) )