classiq 0.93.0__py3-none-any.whl → 0.100.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/__init__.py +11 -19
- classiq/_analyzer_extras/_ipywidgets_async_extension.py +7 -7
- classiq/_analyzer_extras/interactive_hardware.py +19 -12
- classiq/_internals/api_wrapper.py +31 -142
- classiq/_internals/async_utils.py +4 -7
- classiq/_internals/authentication/auth0.py +41 -15
- classiq/_internals/authentication/authorization_code.py +9 -0
- classiq/_internals/authentication/authorization_flow.py +41 -0
- classiq/_internals/authentication/device.py +33 -52
- classiq/_internals/authentication/hybrid_flow.py +19 -0
- classiq/_internals/authentication/password_manager.py +13 -13
- classiq/_internals/authentication/token_manager.py +9 -9
- classiq/_internals/client.py +17 -44
- classiq/_internals/config.py +19 -5
- classiq/_internals/help.py +1 -2
- classiq/_internals/host_checker.py +3 -3
- classiq/_internals/jobs.py +14 -14
- classiq/_internals/type_validation.py +3 -3
- classiq/analyzer/analyzer.py +18 -18
- classiq/analyzer/rb.py +17 -8
- classiq/analyzer/show_interactive_hack.py +1 -1
- classiq/applications/__init__.py +2 -2
- classiq/applications/chemistry/__init__.py +0 -30
- classiq/applications/chemistry/op_utils.py +4 -4
- classiq/applications/chemistry/problems.py +3 -3
- classiq/applications/chemistry/ucc.py +1 -2
- classiq/applications/chemistry/z2_symmetries.py +4 -4
- classiq/applications/combinatorial_helpers/allowed_constraints.py +1 -3
- classiq/applications/combinatorial_helpers/arithmetic/arithmetic_expression.py +2 -1
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +2 -2
- classiq/applications/combinatorial_helpers/encoding_mapping.py +2 -3
- classiq/applications/combinatorial_helpers/encoding_utils.py +2 -2
- classiq/applications/combinatorial_helpers/optimization_model.py +3 -4
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +2 -2
- classiq/applications/combinatorial_helpers/pyomo_utils.py +8 -8
- classiq/applications/combinatorial_helpers/sympy_utils.py +1 -3
- classiq/applications/combinatorial_helpers/transformations/encoding.py +3 -3
- classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +1 -2
- classiq/applications/combinatorial_optimization/combinatorial_optimization_config.py +2 -3
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +4 -6
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +15 -10
- classiq/applications/hamiltonian/pauli_decomposition.py +6 -4
- classiq/applications/iqae/iqae.py +14 -11
- classiq/applications/qnn/datasets/dataset_base_classes.py +6 -6
- classiq/applications/qnn/datasets/dataset_parity.py +6 -6
- classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
- classiq/applications/qnn/qlayer.py +9 -8
- classiq/applications/qnn/torch_utils.py +5 -6
- classiq/applications/qnn/types.py +2 -1
- classiq/applications/qsp/__init__.py +20 -2
- classiq/applications/qsp/qsp.py +238 -10
- classiq/applications/qsvm/qsvm_data_generation.py +1 -2
- classiq/evaluators/classical_expression.py +0 -4
- classiq/evaluators/parameter_types.py +10 -8
- classiq/evaluators/qmod_annotated_expression.py +31 -26
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +14 -14
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +2 -1
- classiq/evaluators/qmod_expression_visitors/sympy_wrappers.py +8 -8
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +4 -4
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +14 -4
- classiq/evaluators/qmod_node_evaluators/list_evaluation.py +2 -2
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +3 -3
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +9 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +6 -6
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +9 -10
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +5 -5
- classiq/execution/__init__.py +0 -3
- classiq/execution/execution_session.py +28 -21
- classiq/execution/jobs.py +26 -26
- classiq/execution/qnn.py +1 -2
- classiq/execution/user_budgets.py +71 -37
- classiq/executor.py +1 -3
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +4 -4
- classiq/interface/analyzer/cytoscape_graph.py +3 -3
- classiq/interface/analyzer/result.py +4 -4
- classiq/interface/ast_node.py +3 -3
- classiq/interface/backend/backend_preferences.py +26 -50
- classiq/interface/backend/ionq/ionq_quantum_program.py +5 -5
- classiq/interface/backend/provider_config/__init__.py +0 -0
- classiq/interface/backend/provider_config/provider_config.py +8 -0
- classiq/interface/backend/provider_config/providers/__init__.py +0 -0
- classiq/interface/backend/provider_config/providers/alice_bob.py +47 -0
- classiq/interface/backend/provider_config/providers/aqt.py +16 -0
- classiq/interface/backend/provider_config/providers/azure.py +37 -0
- classiq/interface/backend/provider_config/providers/braket.py +39 -0
- classiq/interface/backend/provider_config/providers/ibm.py +26 -0
- classiq/interface/backend/provider_config/providers/ionq.py +22 -0
- classiq/interface/backend/quantum_backend_providers.py +20 -2
- classiq/interface/chemistry/ansatz_library.py +3 -5
- classiq/interface/chemistry/operator.py +3 -3
- classiq/interface/combinatorial_optimization/examples/knapsack.py +2 -4
- classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +1 -2
- classiq/interface/compression_utils.py +2 -3
- classiq/interface/debug_info/debug_info.py +8 -7
- classiq/interface/exceptions.py +6 -7
- classiq/interface/execution/primitives.py +6 -6
- classiq/interface/executor/estimate_cost.py +1 -1
- classiq/interface/executor/execution_preferences.py +3 -5
- classiq/interface/executor/execution_request.py +10 -10
- classiq/interface/executor/execution_result.py +1 -2
- classiq/interface/executor/quantum_code.py +8 -8
- classiq/interface/executor/result.py +28 -18
- classiq/interface/executor/user_budget.py +21 -17
- classiq/interface/executor/vqe_result.py +5 -6
- classiq/interface/generator/ansatz_library.py +6 -8
- classiq/interface/generator/application_apis/__init__.py +0 -3
- classiq/interface/generator/arith/arithmetic.py +2 -2
- classiq/interface/generator/arith/arithmetic_arg_type_validator.py +2 -3
- classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -5
- classiq/interface/generator/arith/arithmetic_expression_parser.py +11 -4
- classiq/interface/generator/arith/arithmetic_expression_validator.py +12 -15
- classiq/interface/generator/arith/arithmetic_operations.py +4 -6
- classiq/interface/generator/arith/arithmetic_param_getters.py +70 -107
- classiq/interface/generator/arith/arithmetic_result_builder.py +4 -4
- classiq/interface/generator/arith/ast_node_rewrite.py +8 -4
- classiq/interface/generator/arith/binary_ops.py +15 -40
- classiq/interface/generator/arith/logical_ops.py +2 -3
- classiq/interface/generator/arith/number_utils.py +2 -2
- classiq/interface/generator/arith/register_user_input.py +3 -3
- classiq/interface/generator/arith/unary_ops.py +2 -2
- classiq/interface/generator/circuit_code/circuit_code.py +8 -10
- classiq/interface/generator/circuit_code/types_and_constants.py +1 -1
- classiq/interface/generator/complex_type.py +2 -2
- classiq/interface/generator/copy.py +1 -3
- classiq/interface/generator/expressions/atomic_expression_functions.py +0 -5
- classiq/interface/generator/expressions/evaluated_expression.py +2 -3
- classiq/interface/generator/expressions/expression.py +2 -2
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +4 -7
- classiq/interface/generator/function_param_list.py +0 -40
- classiq/interface/generator/function_params.py +5 -6
- classiq/interface/generator/functions/classical_function_declaration.py +2 -2
- classiq/interface/generator/functions/classical_type.py +3 -3
- classiq/interface/generator/functions/type_modifier.py +0 -15
- classiq/interface/generator/functions/type_name.py +2 -2
- classiq/interface/generator/generated_circuit_data.py +14 -18
- classiq/interface/generator/generation_request.py +35 -0
- classiq/interface/generator/hamiltonian_evolution/exponentiation.py +2 -4
- classiq/interface/generator/hardware/hardware_data.py +8 -8
- classiq/interface/generator/hardware_efficient_ansatz.py +9 -9
- classiq/interface/generator/mcu.py +3 -3
- classiq/interface/generator/mcx.py +3 -3
- classiq/interface/generator/model/constraints.py +34 -5
- classiq/interface/generator/model/preferences/preferences.py +15 -21
- classiq/interface/generator/model/quantum_register.py +7 -10
- classiq/interface/generator/noise_properties.py +3 -7
- classiq/interface/generator/parameters.py +1 -1
- classiq/interface/generator/partitioned_register.py +1 -2
- classiq/interface/generator/preferences/qasm_to_qmod_params.py +11 -0
- classiq/interface/generator/quantum_function_call.py +9 -12
- classiq/interface/generator/quantum_program.py +10 -23
- classiq/interface/generator/range_types.py +3 -3
- classiq/interface/generator/slice_parsing_utils.py +4 -5
- classiq/interface/generator/standard_gates/standard_gates.py +2 -4
- classiq/interface/generator/synthesis_execution_parameter.py +1 -3
- classiq/interface/generator/synthesis_metadata/synthesis_duration.py +9 -0
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +2 -3
- classiq/interface/generator/transpiler_basis_gates.py +10 -4
- classiq/interface/generator/types/builtin_enum_declarations.py +0 -145
- classiq/interface/generator/types/compilation_metadata.py +13 -2
- classiq/interface/generator/types/enum_declaration.py +2 -1
- classiq/interface/generator/validations/flow_graph.py +3 -3
- classiq/interface/generator/visitor.py +10 -12
- classiq/interface/hardware.py +2 -3
- classiq/interface/helpers/classproperty.py +2 -2
- classiq/interface/helpers/custom_encoders.py +2 -1
- classiq/interface/helpers/custom_pydantic_types.py +1 -1
- classiq/interface/helpers/text_utils.py +5 -4
- classiq/interface/ide/visual_model.py +6 -5
- classiq/interface/interface_version.py +1 -1
- classiq/interface/jobs.py +3 -3
- classiq/interface/model/allocate.py +4 -4
- classiq/interface/model/bind_operation.py +3 -0
- classiq/interface/model/block.py +6 -2
- classiq/interface/model/bounds.py +3 -3
- classiq/interface/model/classical_if.py +4 -0
- classiq/interface/model/control.py +8 -1
- classiq/interface/model/inplace_binary_operation.py +2 -2
- classiq/interface/model/invert.py +4 -0
- classiq/interface/model/model.py +4 -4
- classiq/interface/model/model_visitor.py +40 -1
- classiq/interface/model/parameter.py +1 -3
- classiq/interface/model/port_declaration.py +1 -1
- classiq/interface/model/power.py +4 -0
- classiq/interface/model/quantum_expressions/quantum_expression.py +1 -2
- classiq/interface/model/quantum_function_call.py +3 -6
- classiq/interface/model/quantum_function_declaration.py +1 -0
- classiq/interface/model/quantum_lambda_function.py +4 -4
- classiq/interface/model/quantum_statement.py +11 -4
- classiq/interface/model/quantum_type.py +14 -14
- classiq/interface/model/repeat.py +4 -0
- classiq/interface/model/skip_control.py +4 -0
- classiq/interface/model/validation_handle.py +2 -3
- classiq/interface/model/variable_declaration_statement.py +2 -2
- classiq/interface/model/within_apply_operation.py +4 -0
- classiq/interface/pretty_print/expression_to_qmod.py +3 -4
- classiq/interface/server/routes.py +0 -16
- classiq/interface/source_reference.py +3 -4
- classiq/model_expansions/arithmetic.py +11 -7
- classiq/model_expansions/arithmetic_compute_result_attrs.py +40 -28
- classiq/model_expansions/capturing/captured_vars.py +3 -3
- classiq/model_expansions/capturing/mangling_utils.py +1 -2
- classiq/model_expansions/closure.py +12 -11
- classiq/model_expansions/function_builder.py +14 -6
- classiq/model_expansions/generative_functions.py +7 -12
- classiq/model_expansions/interpreters/base_interpreter.py +3 -7
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +2 -1
- classiq/model_expansions/interpreters/generative_interpreter.py +5 -3
- classiq/model_expansions/quantum_operations/allocate.py +4 -4
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +2 -4
- classiq/model_expansions/quantum_operations/call_emitter.py +31 -40
- classiq/model_expansions/quantum_operations/declarative_call_emitter.py +2 -2
- classiq/model_expansions/quantum_operations/emitter.py +3 -5
- classiq/model_expansions/quantum_operations/expression_evaluator.py +3 -3
- classiq/model_expansions/quantum_operations/skip_control_verifier.py +1 -2
- classiq/model_expansions/quantum_operations/variable_decleration.py +2 -2
- classiq/model_expansions/scope.py +7 -7
- classiq/model_expansions/scope_initialization.py +4 -0
- classiq/model_expansions/visitors/symbolic_param_inference.py +6 -6
- classiq/model_expansions/visitors/uncomputation_signature_inference.py +328 -0
- classiq/model_expansions/visitors/variable_references.py +15 -14
- classiq/open_library/functions/__init__.py +41 -11
- classiq/open_library/functions/amplitude_loading.py +81 -0
- classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
- classiq/open_library/functions/encodings.py +182 -0
- classiq/open_library/functions/grover.py +8 -10
- classiq/open_library/functions/lcu.py +47 -18
- classiq/open_library/functions/modular_exponentiation.py +93 -8
- classiq/open_library/functions/qsvt.py +66 -79
- classiq/open_library/functions/qsvt_temp.py +536 -0
- classiq/open_library/functions/state_preparation.py +137 -31
- classiq/qmod/__init__.py +6 -4
- classiq/qmod/builtins/classical_execution_primitives.py +4 -23
- classiq/qmod/builtins/classical_functions.py +1 -42
- classiq/qmod/builtins/enums.py +15 -153
- classiq/qmod/builtins/functions/__init__.py +9 -18
- classiq/qmod/builtins/functions/allocation.py +25 -4
- classiq/qmod/builtins/functions/arithmetic.py +22 -27
- classiq/qmod/builtins/functions/exponentiation.py +51 -2
- classiq/qmod/builtins/functions/mcx_func.py +7 -0
- classiq/qmod/builtins/functions/standard_gates.py +46 -27
- classiq/qmod/builtins/operations.py +165 -79
- classiq/qmod/builtins/structs.py +24 -91
- classiq/qmod/cfunc.py +3 -2
- classiq/qmod/classical_function.py +2 -1
- classiq/qmod/cparam.py +2 -8
- classiq/qmod/create_model_function.py +7 -7
- classiq/qmod/declaration_inferrer.py +33 -30
- classiq/qmod/expression_query.py +7 -4
- classiq/qmod/model_state_container.py +2 -2
- classiq/qmod/native/pretty_printer.py +25 -14
- classiq/qmod/pretty_print/expression_to_python.py +5 -3
- classiq/qmod/pretty_print/pretty_printer.py +39 -17
- classiq/qmod/python_classical_type.py +40 -13
- classiq/qmod/qfunc.py +124 -19
- classiq/qmod/qmod_constant.py +2 -2
- classiq/qmod/qmod_parameter.py +5 -2
- classiq/qmod/qmod_variable.py +48 -47
- classiq/qmod/quantum_callable.py +18 -13
- classiq/qmod/quantum_expandable.py +31 -26
- classiq/qmod/quantum_function.py +84 -36
- classiq/qmod/semantics/annotation/call_annotation.py +5 -5
- classiq/qmod/semantics/error_manager.py +23 -15
- classiq/qmod/semantics/lambdas.py +1 -2
- classiq/qmod/semantics/validation/types_validation.py +1 -2
- classiq/qmod/symbolic.py +2 -4
- classiq/qmod/utilities.py +13 -20
- classiq/qmod/write_qmod.py +3 -4
- classiq/quantum_program.py +1 -3
- classiq/synthesis.py +11 -7
- {classiq-0.93.0.dist-info → classiq-0.100.0.dist-info}/METADATA +2 -3
- {classiq-0.93.0.dist-info → classiq-0.100.0.dist-info}/RECORD +274 -300
- {classiq-0.93.0.dist-info → classiq-0.100.0.dist-info}/WHEEL +1 -1
- classiq/applications/chemistry/ansatz_parameters.py +0 -29
- classiq/applications/chemistry/chemistry_execution_parameters.py +0 -16
- classiq/applications/chemistry/chemistry_model_constructor.py +0 -532
- classiq/applications/chemistry/ground_state_problem.py +0 -42
- classiq/applications/qsvm/__init__.py +0 -8
- classiq/applications/qsvm/qsvm.py +0 -11
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -129
- classiq/execution/iqcc.py +0 -128
- classiq/interface/applications/qsvm.py +0 -117
- classiq/interface/chemistry/elements.py +0 -120
- classiq/interface/chemistry/fermionic_operator.py +0 -208
- classiq/interface/chemistry/ground_state_problem.py +0 -132
- classiq/interface/chemistry/ground_state_result.py +0 -8
- classiq/interface/chemistry/molecule.py +0 -71
- classiq/interface/execution/iqcc.py +0 -44
- classiq/interface/generator/application_apis/chemistry_declarations.py +0 -69
- classiq/interface/generator/application_apis/entangler_declarations.py +0 -29
- classiq/interface/generator/application_apis/qsvm_declarations.py +0 -6
- classiq/interface/generator/chemistry_function_params.py +0 -50
- classiq/interface/generator/entangler_params.py +0 -72
- classiq/interface/generator/entanglers.py +0 -14
- classiq/interface/generator/hamiltonian_evolution/qdrift.py +0 -27
- classiq/interface/generator/hartree_fock.py +0 -26
- classiq/interface/generator/hva.py +0 -22
- classiq/interface/generator/linear_pauli_rotations.py +0 -92
- classiq/interface/generator/qft.py +0 -37
- classiq/interface/generator/qsvm.py +0 -96
- classiq/interface/generator/state_preparation/__init__.py +0 -14
- classiq/interface/generator/state_preparation/bell_state_preparation.py +0 -27
- classiq/interface/generator/state_preparation/computational_basis_state_preparation.py +0 -28
- classiq/interface/generator/state_preparation/distributions.py +0 -53
- classiq/interface/generator/state_preparation/exponential_state_preparation.py +0 -14
- classiq/interface/generator/state_preparation/ghz_state_preparation.py +0 -14
- classiq/interface/generator/state_preparation/metrics.py +0 -41
- classiq/interface/generator/state_preparation/state_preparation.py +0 -113
- classiq/interface/generator/state_preparation/state_preparation_abc.py +0 -24
- classiq/interface/generator/state_preparation/uniform_distibution_state_preparation.py +0 -13
- classiq/interface/generator/state_preparation/w_state_preparation.py +0 -13
- classiq/interface/generator/ucc.py +0 -74
- classiq/interface/helpers/backward_compatibility.py +0 -9
- classiq/model_expansions/transformers/type_modifier_inference.py +0 -392
- classiq/open_library/functions/lookup_table.py +0 -58
- classiq/qmod/builtins/functions/chemistry.py +0 -123
- classiq/qmod/builtins/functions/qsvm.py +0 -24
- {classiq-0.93.0.dist-info → classiq-0.100.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import urllib.parse
|
|
2
|
+
import warnings
|
|
2
3
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
from httpx import AsyncClient, Response, codes
|
|
6
7
|
from pydantic import Field
|
|
@@ -8,6 +9,8 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
8
9
|
|
|
9
10
|
from classiq.interface.exceptions import ClassiqAuthenticationError
|
|
10
11
|
|
|
12
|
+
from classiq._internals.config import CONFIG_ENV_FILES
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
class AuthSettings(BaseSettings):
|
|
13
16
|
domain: str = Field(
|
|
@@ -20,22 +23,18 @@ class AuthSettings(BaseSettings):
|
|
|
20
23
|
default="f6721qMOVoDAOVkzrv8YaWassRKSFX6Y",
|
|
21
24
|
validation_alias="CLASSIQ_AUTH_CLIENT_ID",
|
|
22
25
|
)
|
|
26
|
+
organization: str = Field(default="", validation_alias="CLASSIQ_AUTH_ORGANIZATION")
|
|
23
27
|
|
|
24
|
-
model_config = SettingsConfigDict(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
field: data[field] for field in data if field in self.model_fields
|
|
29
|
-
}
|
|
30
|
-
super().__init__(**data)
|
|
31
|
-
for field, value in initial_data.items():
|
|
32
|
-
setattr(self, field, value)
|
|
28
|
+
model_config = SettingsConfigDict(
|
|
29
|
+
env_file=CONFIG_ENV_FILES,
|
|
30
|
+
extra="ignore",
|
|
31
|
+
)
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
@dataclass
|
|
36
35
|
class Tokens:
|
|
37
36
|
access_token: str
|
|
38
|
-
refresh_token:
|
|
37
|
+
refresh_token: str | None
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
class Auth0:
|
|
@@ -53,11 +52,15 @@ class Auth0:
|
|
|
53
52
|
def _client_id(self) -> str:
|
|
54
53
|
return self._auth_settings.client_id
|
|
55
54
|
|
|
55
|
+
@property
|
|
56
|
+
def _organization(self) -> str:
|
|
57
|
+
return self._auth_settings.organization
|
|
58
|
+
|
|
56
59
|
async def _make_request(
|
|
57
60
|
self,
|
|
58
61
|
url: str,
|
|
59
62
|
payload: dict[str, str],
|
|
60
|
-
allow_error:
|
|
63
|
+
allow_error: bool | int = False,
|
|
61
64
|
) -> dict[str, Any]:
|
|
62
65
|
encoded_payload = urllib.parse.urlencode(payload)
|
|
63
66
|
client: AsyncClient
|
|
@@ -76,13 +79,20 @@ class Auth0:
|
|
|
76
79
|
f"Request to Auth0 failed with error code {code}: {data.get('error')}"
|
|
77
80
|
)
|
|
78
81
|
|
|
79
|
-
async def get_device_data(
|
|
82
|
+
async def get_device_data(
|
|
83
|
+
self, require_refresh_token: bool = True
|
|
84
|
+
) -> dict[str, Any]:
|
|
80
85
|
payload = {
|
|
81
|
-
"client_id": self.
|
|
86
|
+
"client_id": self._client_id,
|
|
82
87
|
"audience": self._auth_settings.audience,
|
|
83
88
|
}
|
|
84
|
-
if
|
|
89
|
+
if require_refresh_token:
|
|
85
90
|
payload["scope"] = "offline_access"
|
|
91
|
+
if self._organization:
|
|
92
|
+
warnings.warn(
|
|
93
|
+
"Organizations are not supported in device auth flow.",
|
|
94
|
+
stacklevel=1,
|
|
95
|
+
)
|
|
86
96
|
|
|
87
97
|
return await self._make_request(
|
|
88
98
|
url="/oauth/device/code",
|
|
@@ -102,6 +112,22 @@ class Auth0:
|
|
|
102
112
|
allow_error=codes.FORBIDDEN,
|
|
103
113
|
)
|
|
104
114
|
|
|
115
|
+
def get_authorize_url(
|
|
116
|
+
self, redirect_uri: str, require_refresh_token: bool = True
|
|
117
|
+
) -> str:
|
|
118
|
+
params = {
|
|
119
|
+
"client_id": self._client_id,
|
|
120
|
+
"response_type": "code",
|
|
121
|
+
"audience": self._auth_settings.audience,
|
|
122
|
+
"redirect_uri": redirect_uri,
|
|
123
|
+
}
|
|
124
|
+
if require_refresh_token:
|
|
125
|
+
params["scope"] = "offline_access"
|
|
126
|
+
if self._organization:
|
|
127
|
+
# Otherwise, let the Auth0 handle
|
|
128
|
+
params["organization"] = self._organization
|
|
129
|
+
return f"{self._base_url}/authorize?{urllib.parse.urlencode(params)}"
|
|
130
|
+
|
|
105
131
|
async def refresh_access_token(self, refresh_token: str) -> Tokens:
|
|
106
132
|
# TODO handle failure
|
|
107
133
|
payload = {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from classiq._internals.authentication.authorization_flow import AuthorizationFlow
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AuthorizationCodeFlow(AuthorizationFlow):
|
|
5
|
+
async def authorize(self, redirect_uri: str) -> None:
|
|
6
|
+
auth_url = self.auth0_client.get_authorize_url(
|
|
7
|
+
redirect_uri, self.require_refresh_token
|
|
8
|
+
)
|
|
9
|
+
self.open_url(auth_url)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import webbrowser
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from classiq.interface.exceptions import ClassiqAuthenticationError
|
|
5
|
+
|
|
6
|
+
from classiq._internals.authentication.auth0 import Auth0, Tokens
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AuthorizationFlow:
|
|
10
|
+
def __init__(self, require_refresh_token: bool = True, text_only: bool = False):
|
|
11
|
+
self.require_refresh_token = require_refresh_token
|
|
12
|
+
self.text_only = text_only
|
|
13
|
+
self.auth0_client = Auth0()
|
|
14
|
+
|
|
15
|
+
async def get_tokens(self) -> Tokens:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
def handle_ready_data(self, data: dict[str, Any]) -> Tokens:
|
|
19
|
+
access_token: str | None = data.get("access_token") or None
|
|
20
|
+
# If refresh token was not requested, this would be None
|
|
21
|
+
refresh_token: str | None = data.get("refresh_token") or None
|
|
22
|
+
|
|
23
|
+
if access_token is None or (
|
|
24
|
+
self.require_refresh_token is True and refresh_token is None
|
|
25
|
+
):
|
|
26
|
+
raise ClassiqAuthenticationError(
|
|
27
|
+
"Token generation failed for unknown reason (missing access token or refresh token)."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return Tokens(access_token=access_token, refresh_token=refresh_token)
|
|
31
|
+
|
|
32
|
+
def open_url(self, url: str) -> None:
|
|
33
|
+
if self.text_only:
|
|
34
|
+
print( # noqa: T201
|
|
35
|
+
f"Please visit this URL from any trusted device to authenticate: {url}"
|
|
36
|
+
)
|
|
37
|
+
else:
|
|
38
|
+
webbrowser.open(url)
|
|
39
|
+
print( # noqa: T201
|
|
40
|
+
f"If a browser doesn't automatically open, please visit this URL from any trusted device to authenticate: {url}"
|
|
41
|
+
)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import webbrowser
|
|
3
2
|
from collections.abc import Iterable
|
|
3
|
+
from dataclasses import dataclass
|
|
4
4
|
from datetime import timedelta
|
|
5
|
-
from typing import Any,
|
|
5
|
+
from typing import Any, TypeVar
|
|
6
6
|
|
|
7
7
|
from classiq.interface.exceptions import (
|
|
8
8
|
ClassiqAuthenticationError,
|
|
@@ -10,74 +10,55 @@ from classiq.interface.exceptions import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
from classiq._internals.async_utils import poll_for
|
|
13
|
-
from classiq._internals.authentication.auth0 import
|
|
13
|
+
from classiq._internals.authentication.auth0 import Tokens
|
|
14
|
+
from classiq._internals.authentication.authorization_flow import AuthorizationFlow
|
|
14
15
|
|
|
15
16
|
T = TypeVar("T")
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
@dataclass
|
|
20
|
+
class DeviceData:
|
|
21
|
+
user_code: str
|
|
22
|
+
device_code: str
|
|
23
|
+
interval: float
|
|
24
|
+
expires_in: float
|
|
25
|
+
verification_uri: str
|
|
26
|
+
verification_uri_complete: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DeviceCodeFlow(AuthorizationFlow):
|
|
19
30
|
_TIMEOUT_ERROR = (
|
|
20
31
|
"Device registration timed out. Please re-initiate the flow and "
|
|
21
32
|
"authorize the device within the timeout."
|
|
22
33
|
)
|
|
23
34
|
_TIMEOUT_SEC: float = timedelta(minutes=15).total_seconds()
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
) -> Tokens:
|
|
29
|
-
auth0_client = Auth0()
|
|
30
|
-
data: dict[str, Any] = await auth0_client.get_device_data(
|
|
31
|
-
get_refresh_token=get_refresh_token
|
|
36
|
+
async def get_device_data(self) -> DeviceData:
|
|
37
|
+
device_data: dict[str, Any] = await self.auth0_client.get_device_data(
|
|
38
|
+
require_refresh_token=self.require_refresh_token
|
|
32
39
|
)
|
|
40
|
+
return DeviceData(**device_data)
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
webbrowser.open(verification_url)
|
|
41
|
-
timeout = min(data["expires_in"], cls._TIMEOUT_SEC)
|
|
42
|
-
return await cls._poll_tokens(
|
|
43
|
-
auth0_client=auth0_client,
|
|
44
|
-
device_code=data["device_code"],
|
|
45
|
-
interval=data["interval"],
|
|
42
|
+
async def poll_tokens(self, device_data: DeviceData) -> Tokens:
|
|
43
|
+
interval = device_data.interval
|
|
44
|
+
timeout = min(device_data.expires_in, self._TIMEOUT_SEC)
|
|
45
|
+
return await self._poll_tokens(
|
|
46
|
+
device_code=device_data.device_code,
|
|
47
|
+
interval=interval,
|
|
46
48
|
timeout=timeout,
|
|
47
|
-
get_refresh_token=get_refresh_token,
|
|
48
49
|
)
|
|
49
50
|
|
|
50
|
-
@classmethod
|
|
51
|
-
def _handle_ready_data(
|
|
52
|
-
cls, data: dict[str, Any], get_refresh_token: bool
|
|
53
|
-
) -> Tokens:
|
|
54
|
-
access_token: Optional[str] = data.get("access_token")
|
|
55
|
-
# If refresh token was not requested, this would be None
|
|
56
|
-
refresh_token: Optional[str] = data.get("refresh_token")
|
|
57
|
-
|
|
58
|
-
if access_token is None or (
|
|
59
|
-
get_refresh_token is True and refresh_token is None
|
|
60
|
-
):
|
|
61
|
-
raise ClassiqAuthenticationError(
|
|
62
|
-
"Token generation failed for unknown reason."
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
return Tokens(access_token=access_token, refresh_token=refresh_token)
|
|
66
|
-
|
|
67
|
-
@classmethod
|
|
68
51
|
async def _poll_tokens(
|
|
69
|
-
|
|
70
|
-
auth0_client: Auth0,
|
|
52
|
+
self,
|
|
71
53
|
device_code: str,
|
|
72
|
-
interval:
|
|
54
|
+
interval: float,
|
|
73
55
|
timeout: float,
|
|
74
|
-
get_refresh_token: bool = True,
|
|
75
56
|
) -> Tokens:
|
|
76
57
|
async def poller() -> dict[str, Any]:
|
|
77
58
|
nonlocal device_code
|
|
78
|
-
return await auth0_client.poll_tokens(device_code=device_code)
|
|
59
|
+
return await self.auth0_client.poll_tokens(device_code=device_code)
|
|
79
60
|
|
|
80
|
-
def interval_coro() -> Iterable[
|
|
61
|
+
def interval_coro() -> Iterable[float]:
|
|
81
62
|
nonlocal interval
|
|
82
63
|
while True:
|
|
83
64
|
yield interval
|
|
@@ -86,16 +67,16 @@ class DeviceRegistrar:
|
|
|
86
67
|
async for data in poll_for(
|
|
87
68
|
poller=poller, timeout_sec=timeout, interval_sec=interval_coro()
|
|
88
69
|
):
|
|
89
|
-
error_code:
|
|
70
|
+
error_code: str | None = data.get("error")
|
|
90
71
|
if error_code is None:
|
|
91
|
-
return
|
|
72
|
+
return self.handle_ready_data(data)
|
|
92
73
|
elif error_code == "authorization_pending":
|
|
93
74
|
pass
|
|
94
75
|
elif error_code == "slow_down":
|
|
95
76
|
# This value is used by poll_for via interval_coro
|
|
96
77
|
interval *= 2
|
|
97
78
|
elif error_code == "expired_token":
|
|
98
|
-
raise ClassiqExpiredTokenError(
|
|
79
|
+
raise ClassiqExpiredTokenError(self._TIMEOUT_ERROR)
|
|
99
80
|
elif error_code == "access_denied":
|
|
100
81
|
error_description = data.get("error_description")
|
|
101
82
|
if error_description is None:
|
|
@@ -109,4 +90,4 @@ class DeviceRegistrar:
|
|
|
109
90
|
f"Device registration failed with an unknown error: {error_code}."
|
|
110
91
|
)
|
|
111
92
|
else:
|
|
112
|
-
raise ClassiqAuthenticationError(
|
|
93
|
+
raise ClassiqAuthenticationError(self._TIMEOUT_ERROR)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from classiq._internals.authentication.auth0 import Tokens
|
|
2
|
+
from classiq._internals.authentication.authorization_code import AuthorizationCodeFlow
|
|
3
|
+
from classiq._internals.authentication.authorization_flow import AuthorizationFlow
|
|
4
|
+
from classiq._internals.authentication.device import DeviceCodeFlow
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HybridFlow(AuthorizationFlow):
|
|
8
|
+
def __init__(
|
|
9
|
+
self, require_refresh_token: bool = True, text_only: bool = False
|
|
10
|
+
) -> None:
|
|
11
|
+
super().__init__(require_refresh_token, text_only)
|
|
12
|
+
self.device_flow = DeviceCodeFlow(require_refresh_token, text_only)
|
|
13
|
+
self.auth_code_flow = AuthorizationCodeFlow(require_refresh_token, text_only)
|
|
14
|
+
|
|
15
|
+
async def get_tokens(self) -> Tokens:
|
|
16
|
+
device_data = await self.device_flow.get_device_data()
|
|
17
|
+
await self.auth_code_flow.authorize(device_data.verification_uri_complete)
|
|
18
|
+
print(f"Your user code: {device_data.user_code}") # noqa: T201
|
|
19
|
+
return await self.device_flow.poll_tokens(device_data)
|
|
@@ -5,7 +5,7 @@ import os
|
|
|
5
5
|
import pathlib
|
|
6
6
|
import platform
|
|
7
7
|
import stat
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
import keyring
|
|
11
11
|
from keyring.backends import fail
|
|
@@ -42,27 +42,27 @@ class PasswordManager(abc.ABC):
|
|
|
42
42
|
self._settings = PasswordManagerSettings()
|
|
43
43
|
|
|
44
44
|
@property
|
|
45
|
-
def access_token(self) ->
|
|
45
|
+
def access_token(self) -> str | None:
|
|
46
46
|
return self._get(key=self._settings.ACCESS_TOKEN_KEY)
|
|
47
47
|
|
|
48
48
|
@access_token.setter
|
|
49
|
-
def access_token(self, access_token:
|
|
49
|
+
def access_token(self, access_token: str | None) -> None:
|
|
50
50
|
self._set(key=self._settings.ACCESS_TOKEN_KEY, value=access_token)
|
|
51
51
|
|
|
52
52
|
@property
|
|
53
|
-
def refresh_token(self) ->
|
|
53
|
+
def refresh_token(self) -> str | None:
|
|
54
54
|
return self._get(key=self._settings.REFRESH_TOKEN_KEY)
|
|
55
55
|
|
|
56
56
|
@refresh_token.setter
|
|
57
|
-
def refresh_token(self, refresh_token:
|
|
57
|
+
def refresh_token(self, refresh_token: str | None) -> None:
|
|
58
58
|
self._set(key=self._settings.REFRESH_TOKEN_KEY, value=refresh_token)
|
|
59
59
|
|
|
60
60
|
@abc.abstractmethod
|
|
61
|
-
def _get(self, key: str) ->
|
|
61
|
+
def _get(self, key: str) -> str | None:
|
|
62
62
|
pass
|
|
63
63
|
|
|
64
64
|
@abc.abstractmethod
|
|
65
|
-
def _set(self, key: str, value:
|
|
65
|
+
def _set(self, key: str, value: str | None) -> None:
|
|
66
66
|
pass
|
|
67
67
|
|
|
68
68
|
@abc.abstractmethod
|
|
@@ -76,10 +76,10 @@ class PasswordManager(abc.ABC):
|
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
class KeyringPasswordManager(PasswordManager):
|
|
79
|
-
def _get(self, key: str) ->
|
|
79
|
+
def _get(self, key: str) -> str | None:
|
|
80
80
|
return keyring.get_password(service_name=self._SERVICE_NAME, username=key)
|
|
81
81
|
|
|
82
|
-
def _set(self, key: str, value:
|
|
82
|
+
def _set(self, key: str, value: str | None) -> None:
|
|
83
83
|
if value is None:
|
|
84
84
|
self._clear(key)
|
|
85
85
|
return
|
|
@@ -101,10 +101,10 @@ class KeyringPasswordManager(PasswordManager):
|
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
class DummyPasswordManager(PasswordManager):
|
|
104
|
-
def _get(self, key: str) ->
|
|
104
|
+
def _get(self, key: str) -> str | None:
|
|
105
105
|
return None
|
|
106
106
|
|
|
107
|
-
def _set(self, key: str, value:
|
|
107
|
+
def _set(self, key: str, value: str | None) -> None:
|
|
108
108
|
return
|
|
109
109
|
|
|
110
110
|
def _clear(self, key: str) -> None:
|
|
@@ -134,13 +134,13 @@ class FilePasswordManager(PasswordManager):
|
|
|
134
134
|
return json.loads(self.credentials_file.read_text())
|
|
135
135
|
return {}
|
|
136
136
|
|
|
137
|
-
def _get(self, key: str) ->
|
|
137
|
+
def _get(self, key: str) -> str | None:
|
|
138
138
|
token_dict = self._get_token_dict()
|
|
139
139
|
if key in token_dict:
|
|
140
140
|
return token_dict[key]
|
|
141
141
|
return None
|
|
142
142
|
|
|
143
|
-
def _set(self, key: str, value:
|
|
143
|
+
def _set(self, key: str, value: str | None) -> None:
|
|
144
144
|
token_dict = self._get_token_dict()
|
|
145
145
|
token_dict[key] = value
|
|
146
146
|
self._update_file(token_dict)
|
|
@@ -3,7 +3,6 @@ import logging
|
|
|
3
3
|
import threading
|
|
4
4
|
import warnings
|
|
5
5
|
from collections.abc import Sequence
|
|
6
|
-
from typing import Optional
|
|
7
6
|
|
|
8
7
|
from classiq.interface.exceptions import (
|
|
9
8
|
ClassiqAuthenticationError,
|
|
@@ -11,8 +10,8 @@ from classiq.interface.exceptions import (
|
|
|
11
10
|
)
|
|
12
11
|
|
|
13
12
|
from classiq._internals.authentication import password_manager as pm
|
|
14
|
-
from classiq._internals.authentication.auth0 import Auth0
|
|
15
|
-
from classiq._internals.authentication.
|
|
13
|
+
from classiq._internals.authentication.auth0 import Auth0, Tokens
|
|
14
|
+
from classiq._internals.authentication.hybrid_flow import HybridFlow
|
|
16
15
|
from classiq._internals.config import Configuration
|
|
17
16
|
|
|
18
17
|
PASSWORD_MANAGERS: Sequence[type[pm.PasswordManager]] = [
|
|
@@ -26,7 +25,7 @@ class TokenManager:
|
|
|
26
25
|
def __init__(
|
|
27
26
|
self,
|
|
28
27
|
config: Configuration,
|
|
29
|
-
password_manager:
|
|
28
|
+
password_manager: pm.PasswordManager | None = None,
|
|
30
29
|
) -> None:
|
|
31
30
|
self._config = config
|
|
32
31
|
if password_manager is None:
|
|
@@ -35,8 +34,8 @@ class TokenManager:
|
|
|
35
34
|
# to a specific event loop, which is undesirable
|
|
36
35
|
self._lock = threading.Lock()
|
|
37
36
|
self._password_manager: pm.PasswordManager = password_manager
|
|
38
|
-
self._access_token:
|
|
39
|
-
self._refresh_token:
|
|
37
|
+
self._access_token: str | None = self._password_manager.access_token
|
|
38
|
+
self._refresh_token: str | None = self._password_manager.refresh_token
|
|
40
39
|
if self._access_token is None and self._refresh_token is not None:
|
|
41
40
|
_logger.info(
|
|
42
41
|
"Inconsistent state, access token is absent and refresh token is present."
|
|
@@ -67,7 +66,7 @@ class TokenManager:
|
|
|
67
66
|
"Password Manager not found, we could not store your credentials securely. Please contact support@classiq.io"
|
|
68
67
|
)
|
|
69
68
|
|
|
70
|
-
def get_access_token(self) ->
|
|
69
|
+
def get_access_token(self) -> str | None:
|
|
71
70
|
return self._access_token
|
|
72
71
|
|
|
73
72
|
async def _refresh(self) -> None:
|
|
@@ -127,7 +126,8 @@ class TokenManager:
|
|
|
127
126
|
async def _authentication_helper(self) -> None:
|
|
128
127
|
# TODO: consider using refresh token rotation
|
|
129
128
|
# (https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation)
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
authorization_flow = HybridFlow(
|
|
130
|
+
require_refresh_token=True, text_only=self._config.text_only
|
|
132
131
|
)
|
|
132
|
+
tokens = await authorization_flow.get_tokens()
|
|
133
133
|
self._save_tokens(tokens, force_override_refresh_token=True)
|
classiq/_internals/client.py
CHANGED
|
@@ -7,9 +7,8 @@ import os
|
|
|
7
7
|
import platform
|
|
8
8
|
import sys
|
|
9
9
|
import time
|
|
10
|
-
from collections.abc import Awaitable
|
|
11
|
-
from
|
|
12
|
-
from typing import Any, Callable, NoReturn, Optional, TypeVar, Union
|
|
10
|
+
from collections.abc import Awaitable, Callable
|
|
11
|
+
from typing import Any, NoReturn, TypeVar
|
|
13
12
|
|
|
14
13
|
import httpx
|
|
15
14
|
from typing_extensions import ParamSpec
|
|
@@ -146,27 +145,6 @@ def try_again_on_failure(
|
|
|
146
145
|
return wrapper
|
|
147
146
|
|
|
148
147
|
|
|
149
|
-
class _AsyncNullContext(contextlib.AbstractAsyncContextManager):
|
|
150
|
-
"""
|
|
151
|
-
This class is meant to replace `contextlib.nullcontext`, which hadn't supported
|
|
152
|
-
async context manager until python 3.10.
|
|
153
|
-
"""
|
|
154
|
-
|
|
155
|
-
def __init__(self, enter_result: Any = None) -> None:
|
|
156
|
-
self._enter_result = enter_result
|
|
157
|
-
|
|
158
|
-
async def __aenter__(self) -> Any:
|
|
159
|
-
return self._enter_result
|
|
160
|
-
|
|
161
|
-
async def __aexit__(
|
|
162
|
-
self,
|
|
163
|
-
exc_type: Optional[type[BaseException]],
|
|
164
|
-
exc_val: Optional[BaseException],
|
|
165
|
-
exc_tb: Optional[TracebackType],
|
|
166
|
-
) -> None:
|
|
167
|
-
pass
|
|
168
|
-
|
|
169
|
-
|
|
170
148
|
class Client:
|
|
171
149
|
_UNKNOWN_VERSION = HostChecker._UNKNOWN_VERSION
|
|
172
150
|
_SESSION_HEADER = "Classiq-Session"
|
|
@@ -177,7 +155,7 @@ class Client:
|
|
|
177
155
|
self._config = conf
|
|
178
156
|
self._token_manager = token_manager.TokenManager(config=self._config)
|
|
179
157
|
self._api_prefix = self._make_api_prefix()
|
|
180
|
-
self._session_id:
|
|
158
|
+
self._session_id: str | None = None
|
|
181
159
|
|
|
182
160
|
@staticmethod
|
|
183
161
|
def _make_api_prefix() -> str:
|
|
@@ -229,9 +207,9 @@ class Client:
|
|
|
229
207
|
http_client: httpx.AsyncClient,
|
|
230
208
|
method: str,
|
|
231
209
|
url: str,
|
|
232
|
-
json:
|
|
233
|
-
params:
|
|
234
|
-
headers:
|
|
210
|
+
json: dict | None = None,
|
|
211
|
+
params: dict | None = None,
|
|
212
|
+
headers: dict[str, str] | None = None,
|
|
235
213
|
) -> httpx.Response:
|
|
236
214
|
http_client.headers.update(self._get_headers())
|
|
237
215
|
|
|
@@ -265,12 +243,12 @@ class Client:
|
|
|
265
243
|
self,
|
|
266
244
|
http_method: str,
|
|
267
245
|
url: str,
|
|
268
|
-
body:
|
|
269
|
-
params:
|
|
246
|
+
body: dict | None = None,
|
|
247
|
+
params: dict | None = None,
|
|
270
248
|
use_versioned_url: bool = True,
|
|
271
|
-
headers:
|
|
272
|
-
http_client:
|
|
273
|
-
) ->
|
|
249
|
+
headers: dict[str, str] | None = None,
|
|
250
|
+
http_client: httpx.AsyncClient | None = None,
|
|
251
|
+
) -> dict | list | str:
|
|
274
252
|
if use_versioned_url:
|
|
275
253
|
url = self.make_versioned_url(url)
|
|
276
254
|
async with self.use_client_or_create(http_client) as async_client:
|
|
@@ -285,26 +263,21 @@ class Client:
|
|
|
285
263
|
return response.json()
|
|
286
264
|
|
|
287
265
|
def use_client_or_create(
|
|
288
|
-
self, http_client:
|
|
266
|
+
self, http_client: httpx.AsyncClient | None
|
|
289
267
|
) -> contextlib.AbstractAsyncContextManager[httpx.AsyncClient]:
|
|
290
268
|
if http_client is None:
|
|
291
269
|
return self.async_client()
|
|
292
270
|
else:
|
|
293
|
-
|
|
294
|
-
# remove this `if` and the `_AsyncNullContext` class when we stop
|
|
295
|
-
# supporting python 3.9
|
|
296
|
-
return _AsyncNullContext(enter_result=http_client)
|
|
297
|
-
else:
|
|
298
|
-
return contextlib.nullcontext(enter_result=http_client)
|
|
271
|
+
return contextlib.nullcontext(enter_result=http_client)
|
|
299
272
|
|
|
300
273
|
def sync_call_api(
|
|
301
274
|
self,
|
|
302
275
|
http_method: str,
|
|
303
276
|
url: str,
|
|
304
|
-
body:
|
|
305
|
-
headers:
|
|
277
|
+
body: dict | None = None,
|
|
278
|
+
headers: dict | None = None,
|
|
306
279
|
use_versioned_url: bool = True,
|
|
307
|
-
) ->
|
|
280
|
+
) -> dict | str:
|
|
308
281
|
if use_versioned_url:
|
|
309
282
|
url = self.make_versioned_url(url)
|
|
310
283
|
with httpx.Client(**self._make_client_args()) as sync_client:
|
|
@@ -356,7 +329,7 @@ class Client:
|
|
|
356
329
|
return self._config
|
|
357
330
|
|
|
358
331
|
|
|
359
|
-
DEFAULT_CLIENT:
|
|
332
|
+
DEFAULT_CLIENT: Client | None = None
|
|
360
333
|
|
|
361
334
|
|
|
362
335
|
def client() -> Client:
|
classiq/_internals/config.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import pathlib
|
|
5
|
-
from typing import Optional, Union
|
|
6
5
|
|
|
7
6
|
import configargparse # type: ignore[import]
|
|
8
7
|
import pydantic
|
|
@@ -37,7 +36,21 @@ class Configuration(BaseModel):
|
|
|
37
36
|
)
|
|
38
37
|
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
home_path = pathlib.Path.home()
|
|
40
|
+
if os.name == "posix":
|
|
41
|
+
config_path = home_path / ".config"
|
|
42
|
+
else: # assuming "nt"
|
|
43
|
+
app_data = os.getenv("APPDATA")
|
|
44
|
+
if app_data:
|
|
45
|
+
config_path = pathlib.Path(app_data)
|
|
46
|
+
else:
|
|
47
|
+
config_path = home_path / "AppData" / "Roaming"
|
|
48
|
+
|
|
49
|
+
_DEFAULT_CONFIG_FILES = [
|
|
50
|
+
str(config_path / "classiq" / "config.ini"),
|
|
51
|
+
str(config_path / "classiq.conf"),
|
|
52
|
+
str(pathlib.Path("classiq", "config.ini")),
|
|
53
|
+
]
|
|
41
54
|
if os.name == "posix":
|
|
42
55
|
# Unix convensions:
|
|
43
56
|
# System-wide configuration rests in "/etc"
|
|
@@ -48,12 +61,13 @@ if os.name == "posix":
|
|
|
48
61
|
_DEFAULT_CONFIG_FILES = [
|
|
49
62
|
"/etc/classiq/config.ini",
|
|
50
63
|
"/etc/classiq.conf",
|
|
51
|
-
"~/.config/classiq/config.ini",
|
|
52
|
-
"~/.config/classiq.conf",
|
|
53
64
|
] + _DEFAULT_CONFIG_FILES
|
|
54
65
|
|
|
55
66
|
|
|
56
|
-
|
|
67
|
+
CONFIG_ENV_FILES = (config_path / "classiq" / "config.env",)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def init(args: str | list[str] | None = None) -> Configuration:
|
|
57
71
|
"""Initialize the configuration object.
|
|
58
72
|
|
|
59
73
|
Args:
|
classiq/_internals/help.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import webbrowser
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
from classiq.interface._version import SEMVER_VERSION
|
|
5
4
|
|
|
6
5
|
DOCS_BASE_URL = "https://docs.classiq.io/"
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
def open_help(version:
|
|
8
|
+
def open_help(version: str | None = None) -> None:
|
|
10
9
|
if version is None:
|
|
11
10
|
version = SEMVER_VERSION
|
|
12
11
|
if version == "0.0.0":
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
import warnings
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
import httpx
|
|
9
9
|
import pydantic
|
|
@@ -38,7 +38,7 @@ class HostChecker:
|
|
|
38
38
|
self._client_version = client_version
|
|
39
39
|
self._interface_version = interface_version
|
|
40
40
|
|
|
41
|
-
def _get_interface_version(self) ->
|
|
41
|
+
def _get_interface_version(self) -> str | None:
|
|
42
42
|
global_interfaces = GlobalVersions.model_validate(
|
|
43
43
|
self._client.sync_call_api(
|
|
44
44
|
"get", "/interface_versions", use_versioned_url=False
|
|
@@ -52,7 +52,7 @@ class HostChecker:
|
|
|
52
52
|
)
|
|
53
53
|
return host.classiq_interface
|
|
54
54
|
|
|
55
|
-
def _get_deprecation_info(self) ->
|
|
55
|
+
def _get_deprecation_info(self) -> DeprecationInfo | None:
|
|
56
56
|
global_versions = GlobalVersions.model_validate(
|
|
57
57
|
self._client.sync_call_api("get", "/versions", use_versioned_url=False)
|
|
58
58
|
)
|