thrustlab 0.3.0__tar.gz → 0.3.2__tar.gz
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.
- {thrustlab-0.3.0 → thrustlab-0.3.2}/CHANGELOG.md +54 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/PKG-INFO +19 -15
- {thrustlab-0.3.0 → thrustlab-0.3.2}/README.md +17 -13
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/dynamic/run_and_poll.py +18 -8
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/outputs/read_dynamic.py +11 -5
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/outputs/read_steady_state.py +1 -1
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/quickstart.py +1 -1
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/simulations/run_sync.py +4 -4
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/sweeps/run_and_poll.py +1 -1
- {thrustlab-0.3.0 → thrustlab-0.3.2}/pyproject.toml +1 -1
- {thrustlab-0.3.0 → thrustlab-0.3.2}/scripts/publish_public_repo.sh +3 -2
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_projects.py +4 -2
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_simulations.py +4 -1
- thrustlab-0.3.2/tests/test_resources/test_starred_components.py +74 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_sweeps.py +4 -1
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_http.py +3 -2
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_version.py +1 -1
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/dynamic_simulations.py +3 -1
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/projects.py +8 -3
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/simulations.py +9 -1
- thrustlab-0.3.2/thrustlab/resources/starred_components.py +95 -0
- thrustlab-0.3.2/thrustlab/resources/submissions.py +94 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/sweeps.py +3 -1
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/webhook_endpoints.py +35 -10
- thrustlab-0.3.0/tests/test_resources/test_starred_components.py +0 -37
- thrustlab-0.3.0/thrustlab/resources/starred_components.py +0 -57
- thrustlab-0.3.0/thrustlab/resources/submissions.py +0 -44
- {thrustlab-0.3.0 → thrustlab-0.3.2}/.gitignore +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/.impeccable/hook.cache.json +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/LICENSE +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/Makefile +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/NOTICE +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/ci/public-ci.yml +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/components/create_component.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/projects/create.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/projects/list.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/projects/retrieve.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/simulations/wait.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/webhooks/verify_handler.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/scripts/regen_models.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/__init__.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/conftest.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_async_polling.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_client.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_components_create.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_components_find.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_dynamic_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_exceptions.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_http.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_pagination.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/__init__.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_components.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_compute_units.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_submissions.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_users.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_webhook_endpoints.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_spec_coverage.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_webhooks.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/__init__.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_async_polling.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/__init__.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/airfoil_breakpoint.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/airfoil_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/airfoil_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_geometry_request.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_geometry_request_mode.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_geometry_request_target_type_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_geometry_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_station_row.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/body_import_airfoil_v1_airfoils_import_post.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/calibrate_accepted_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_brand.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_brands_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_create_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_create_body_spec_json.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_ids_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_patch_spec_json_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_resource_spec_json.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_sweep_axis_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_sweep_axis_in_axis.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_write_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_write_response_spec_json.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_write_warning.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/create_webhook_endpoint_v1_webhook_endpoints_post_response_create_webhook_endpoint_v1_webhook_endpoints_post.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_balance_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_bucket_breakdown.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_bucket_breakdown_type.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_event_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_event_resource_type.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_event_resource_unit_type.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_related_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_related_resource_object.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_summary_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_summary_resource_window.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/design_request.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/design_request_target_mode.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_create_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_estimate_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_estimate_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_display_labels_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_error_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_input_snapshot_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_result_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_status.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_rotor_group_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_rotor_group_in_motor_cooling_source.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/entitlements_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/export_geometry_request.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/export_geometry_request_format.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/export_geometry_request_rotation_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/fire_test_event_v1_webhook_endpoints_public_id_test_post_response_fire_test_event_v1_webhook_endpoints_public_id_test_post.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/generate_geometry_request.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/geometry_preview_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/geometry_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/geometry_style_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/geometry_styles_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get_response_get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_event_v1_events_event_id_get_response_get_event_v1_events_event_id_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_public_result_v1_public_results_slug_get_response_get_public_result_v1_public_results_slug_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_shared_result_v1_public_share_share_token_get_response_get_shared_result_v1_public_share_share_token_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_webhook_endpoint_v1_webhook_endpoints_public_id_get_response_get_webhook_endpoint_v1_webhook_endpoints_public_id_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/http_validation_error.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/list_deliveries_v1_webhook_endpoints_public_id_deliveries_get_response_list_deliveries_v1_webhook_endpoints_public_id_deliveries_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/list_events_v1_events_get_response_list_events_v1_events_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/list_public_propellers_v1_public_propellers_get_response_list_public_propellers_v1_public_propellers_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/list_webhook_endpoints_v1_webhook_endpoints_get_response_list_webhook_endpoints_v1_webhook_endpoints_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch_response_patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/preview_request.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/project_create_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/project_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/project_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/project_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post_response_retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post_response_rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_in_esc_timing.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_in_esc_type.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_in_motor_cooling_source.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/run_queued_v1v1_simulations_run_queued_post_response_run_queued_v1v1_simulations_run_queued_post.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/run_selected_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/run_selected_v1v1_simulations_run_selected_post_response_run_selected_v1v1_simulations_run_selected_post.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/schedule_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/schedule_in_interpolation_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/schedule_in_mode.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_group_command_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_group_command_in_throttle_ramp.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_group_command_in_tilt_ramp.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in_airspeed_ramp.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in_per_group.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in_throttle_ramp.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in_vertical_speed_ramp.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/share_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_cancel_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body_cooling_source_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body_flight_regime.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body_inflow_mode.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body_launch_intent.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_inputs.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_patch_plot_config_json_type_0_item.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_display_labels_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_error_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_input_snapshot_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_plot_config_json_type_0_item.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_result_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_result_type_0_additional_property.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_status.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/spline_data.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/starred_component_create_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/starred_component_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/starred_component_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_create_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_create_body_data_json.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_patch_data_json_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_resource_data_json.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_cancel_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_config_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_config_in_esc_timing_values_type_0_item.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_create_body.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_create_body_cooling_source.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_create_body_inflow_mode.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_create_body_launch_intent.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_inputs.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_param_range_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_param_range_in_mode.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_patch_plot_config_json_type_0_item.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_list_response.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_list_response_display_labels_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_resource_inputs.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_resource_rotors_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_resource_rotors_type_0_additional_property.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_error_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_input_snapshot_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_plot_config_json_type_0_item.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_status.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_summary_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_sweep_config_type_0.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_rotor_group_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_rotor_group_in_esc_timing.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_rotor_group_in_esc_type.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_rotor_group_in_motor_cooling_source.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/termination_in.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/termination_in_mode.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/types.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/user_resource.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/user_resource_unit_system.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/v1_custom_component_override.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/v1_custom_component_override_spec_json.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/v1_health_v1_health_get_response_v1_health_v1_health_get.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/validation_error.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/webhook_endpoint_create.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/webhook_endpoint_patch.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_pagination.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/client.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/exceptions.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/__init__.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/_base.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/components.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/compute_units.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/users.py +0 -0
- {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/webhooks.py +0 -0
|
@@ -8,6 +8,60 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.3.2] - 2026-07-16
|
|
12
|
+
|
|
13
|
+
Bug-fix release: a docs-vs-code audit found several SDK surfaces that
|
|
14
|
+
silently did nothing (the server ignores unknown query params) or targeted
|
|
15
|
+
endpoints that don't exist. No new API surface beyond the fixes below.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **`list(project_id=...)` on `simulations`, `sweeps`, and
|
|
20
|
+
`dynamic_simulations` now actually filters.** The SDK sent a `project_id`
|
|
21
|
+
query key but the endpoints read `project` — the filter was silently
|
|
22
|
+
ignored and every listing returned ALL of the caller's runs. The kwarg
|
|
23
|
+
name is unchanged; the SDK now sends the right param.
|
|
24
|
+
- **`projects.list`, `submissions.list`, `webhook_endpoints.list`,
|
|
25
|
+
`list_events`, and `list_deliveries` pagination kwarg renamed
|
|
26
|
+
`starting_after=` → `cursor=`.** The endpoints read `cursor`;
|
|
27
|
+
`starting_after` was silently ignored (same defect the 0.3.0 changelog
|
|
28
|
+
fixed for sims/sweeps — these five were missed).
|
|
29
|
+
- **BREAKING — `client.starred_components` rewritten against the real API.**
|
|
30
|
+
The previous resource targeted `GET/POST /v1/projects/{pid}/starred-components`,
|
|
31
|
+
which does not exist under `/v1/` — every call 404'd. Stars are flat,
|
|
32
|
+
first-class resources:
|
|
33
|
+
- `list(project_id=..., component_type=..., cursor=...)` →
|
|
34
|
+
`GET /v1/starred_components`;
|
|
35
|
+
- `add(project_id=..., component_id=..., component_type=...)` →
|
|
36
|
+
`POST /v1/starred_components` (component_type is required; duplicate
|
|
37
|
+
star raises `ConflictError` 409 `already_starred`);
|
|
38
|
+
- `remove(star_id)` → `DELETE /v1/starred_components/{star_id}` — keys on
|
|
39
|
+
the star's own `star_<ksuid>` id (from `list()`/`add()`), not the
|
|
40
|
+
component id.
|
|
41
|
+
- **`APIError.request_id` is now populated.** It is read from the error
|
|
42
|
+
envelope's `request_id` (with the `X-Request-ID` response header as a
|
|
43
|
+
fallback); previously the SDK only read the header, which the server did
|
|
44
|
+
not send, so `request_id` was always `None`.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- `simulations.list(status=...)` — filter by `queued` / `running` /
|
|
49
|
+
`completed` / `failed` / `canceled`.
|
|
50
|
+
- `submissions.list(status=...)` — filter by moderation status.
|
|
51
|
+
- `submissions.update(id, ...)` / `submissions.withdraw(id)` — edit or
|
|
52
|
+
withdraw a submission while it is still `submitted`.
|
|
53
|
+
- `webhook_endpoints.list_deliveries(..., event_type=..., created_at_gte=...)`
|
|
54
|
+
— the endpoint's remaining filters, previously unreachable from the SDK.
|
|
55
|
+
|
|
56
|
+
## [0.3.1] - 2026-07-16
|
|
57
|
+
|
|
58
|
+
Packaging-metadata fix, no code changes:
|
|
59
|
+
|
|
60
|
+
- The `Repository` project URL shipped in 0.3.0 carried a literal
|
|
61
|
+
`<owner>` placeholder; it now points to the public SDK repo,
|
|
62
|
+
`https://github.com/kbedrich/thrustlab-sdk`.
|
|
63
|
+
- README links corrected to the same repo.
|
|
64
|
+
|
|
11
65
|
## [0.3.0] - 2026-07-15
|
|
12
66
|
|
|
13
67
|
The ThrustLab public API was refocused to a **product-only** surface: account,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thrustlab
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Official Python SDK for the ThrustLab API
|
|
5
5
|
Project-URL: Homepage, https://thrustlab.com
|
|
6
6
|
Project-URL: Documentation, https://thrustlab.com/docs
|
|
7
|
-
Project-URL: Repository, https://github.com
|
|
7
|
+
Project-URL: Repository, https://github.com/kbedrich/thrustlab-sdk
|
|
8
8
|
Project-URL: Changelog, https://thrustlab.com/docs/changelog
|
|
9
9
|
Author-email: ThrustLab <support@thrustlab.com>
|
|
10
10
|
License: Apache-2.0
|
|
@@ -34,7 +34,10 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
|
|
35
35
|
[](https://pypi.org/project/thrustlab/)
|
|
36
36
|
[](https://pypi.org/project/thrustlab/)
|
|
37
|
-
[](https://github.com/
|
|
37
|
+
[](https://github.com/kbedrich/thrustlab-sdk/blob/main/LICENSE)
|
|
38
|
+
|
|
39
|
+
The official Python client for [ThrustLab](https://thrustlab.com) —
|
|
40
|
+
electric-UAV powertrain simulation as an API.
|
|
38
41
|
|
|
39
42
|
## Install
|
|
40
43
|
|
|
@@ -88,18 +91,17 @@ Runnable end-to-end examples for every simulation type live in
|
|
|
88
91
|
|
|
89
92
|
## Why the SDK (and not `urllib` / `wget`)?
|
|
90
93
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`
|
|
95
|
-
**never reaches the ThrustLab API**, so you get an opaque HTML *error 1010*
|
|
96
|
-
challenge page — **not** a JSON error envelope. The API cannot return a
|
|
97
|
-
structured error for a request it never sees.
|
|
94
|
+
The SDK gives you automatic retries with backoff, idempotency keys, cursor
|
|
95
|
+
pagination, polling helpers, webhook signature verification, and typed
|
|
96
|
+
errors. It is built on [`httpx`](https://www.python-httpx.org/) and sends
|
|
97
|
+
its own `thrustlab-python/<version>` User-Agent.
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
If you hand-roll a client anyway, use an HTTP library with a descriptive
|
|
100
|
+
User-Agent (`httpx`, `requests`, or `curl`). ThrustLab's edge protection
|
|
101
|
+
screens non-browser User-Agents such as stdlib `Python-urllib/*` and `wget`:
|
|
102
|
+
API paths under `/v1/` are exempt from the screening, but every other route
|
|
103
|
+
blocks these at the edge with an opaque *error 1010* page — the request never
|
|
104
|
+
reaches ThrustLab, so no JSON error envelope is possible there.
|
|
103
105
|
|
|
104
106
|
## Configuration
|
|
105
107
|
|
|
@@ -216,11 +218,13 @@ client = Client(max_retries=0)
|
|
|
216
218
|
|
|
217
219
|
## Links
|
|
218
220
|
|
|
221
|
+
- Website: https://thrustlab.com
|
|
219
222
|
- Documentation: https://thrustlab.com/docs
|
|
220
223
|
- API reference: https://thrustlab.com/docs/reference
|
|
221
224
|
- SDK guide: https://thrustlab.com/docs/sdk/python
|
|
222
225
|
- Changelog: https://thrustlab.com/docs/changelog
|
|
223
|
-
-
|
|
226
|
+
- Source: https://github.com/kbedrich/thrustlab-sdk
|
|
227
|
+
- Issue tracker: https://github.com/kbedrich/thrustlab-sdk/issues
|
|
224
228
|
|
|
225
229
|
## License
|
|
226
230
|
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/thrustlab/)
|
|
4
4
|
[](https://pypi.org/project/thrustlab/)
|
|
5
|
-
[](https://github.com/
|
|
5
|
+
[](https://github.com/kbedrich/thrustlab-sdk/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
The official Python client for [ThrustLab](https://thrustlab.com) —
|
|
8
|
+
electric-UAV powertrain simulation as an API.
|
|
6
9
|
|
|
7
10
|
## Install
|
|
8
11
|
|
|
@@ -56,18 +59,17 @@ Runnable end-to-end examples for every simulation type live in
|
|
|
56
59
|
|
|
57
60
|
## Why the SDK (and not `urllib` / `wget`)?
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
`
|
|
63
|
-
**never reaches the ThrustLab API**, so you get an opaque HTML *error 1010*
|
|
64
|
-
challenge page — **not** a JSON error envelope. The API cannot return a
|
|
65
|
-
structured error for a request it never sees.
|
|
62
|
+
The SDK gives you automatic retries with backoff, idempotency keys, cursor
|
|
63
|
+
pagination, polling helpers, webhook signature verification, and typed
|
|
64
|
+
errors. It is built on [`httpx`](https://www.python-httpx.org/) and sends
|
|
65
|
+
its own `thrustlab-python/<version>` User-Agent.
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
If you hand-roll a client anyway, use an HTTP library with a descriptive
|
|
68
|
+
User-Agent (`httpx`, `requests`, or `curl`). ThrustLab's edge protection
|
|
69
|
+
screens non-browser User-Agents such as stdlib `Python-urllib/*` and `wget`:
|
|
70
|
+
API paths under `/v1/` are exempt from the screening, but every other route
|
|
71
|
+
blocks these at the edge with an opaque *error 1010* page — the request never
|
|
72
|
+
reaches ThrustLab, so no JSON error envelope is possible there.
|
|
71
73
|
|
|
72
74
|
## Configuration
|
|
73
75
|
|
|
@@ -184,11 +186,13 @@ client = Client(max_retries=0)
|
|
|
184
186
|
|
|
185
187
|
## Links
|
|
186
188
|
|
|
189
|
+
- Website: https://thrustlab.com
|
|
187
190
|
- Documentation: https://thrustlab.com/docs
|
|
188
191
|
- API reference: https://thrustlab.com/docs/reference
|
|
189
192
|
- SDK guide: https://thrustlab.com/docs/sdk/python
|
|
190
193
|
- Changelog: https://thrustlab.com/docs/changelog
|
|
191
|
-
-
|
|
194
|
+
- Source: https://github.com/kbedrich/thrustlab-sdk
|
|
195
|
+
- Issue tracker: https://github.com/kbedrich/thrustlab-sdk/issues
|
|
192
196
|
|
|
193
197
|
## License
|
|
194
198
|
|
|
@@ -5,8 +5,9 @@ Run it:
|
|
|
5
5
|
python examples/dynamic/run_and_poll.py
|
|
6
6
|
|
|
7
7
|
A dynamic run integrates the powertrain through a throttle/airspeed *schedule*
|
|
8
|
-
until a *termination* condition (here:
|
|
9
|
-
|
|
8
|
+
until a *termination* condition (here: a 2 s spin-up ramp, then a 20 s hover
|
|
9
|
+
at 70% — a fixed-duration mission). It returns per-step `samples` (each a full
|
|
10
|
+
steady-shaped result),
|
|
10
11
|
time `series`, a `scorecard`, and `events`. Swap in your own component IDs, or
|
|
11
12
|
resolve them by name with client.components.find(...).
|
|
12
13
|
"""
|
|
@@ -20,7 +21,7 @@ project = client.projects.create(name="sdk dynamic example")
|
|
|
20
21
|
# Resolve by name, or paste explicit IDs: motor_id = "comp_motor_xxx"
|
|
21
22
|
motor = client.components.find(name="Spektrum Avian 4260 800Kv")
|
|
22
23
|
prop = client.components.find(name="10.5x4.5")
|
|
23
|
-
battery = client.components.find(name="4S
|
|
24
|
+
battery = client.components.find(name="Liperior 5000mAh 4S 35C")
|
|
24
25
|
|
|
25
26
|
dyn = client.dynamic_simulations.create(
|
|
26
27
|
project_id=project["id"],
|
|
@@ -36,19 +37,28 @@ dyn = client.dynamic_simulations.create(
|
|
|
36
37
|
"propeller_component_id": prop["id"],
|
|
37
38
|
}
|
|
38
39
|
],
|
|
39
|
-
#
|
|
40
|
+
# Ramp to 70% over 2 s, then hold for 20 s. The soft-start matters:
|
|
41
|
+
# stepping 70% onto a stationary rotor draws an in-rush that can sag the
|
|
42
|
+
# pack below the low-voltage cutoff and end the run immediately — exactly
|
|
43
|
+
# as it would on real hardware. Endurance ("until depleted") missions work
|
|
44
|
+
# the same way but integrate minutes of pack time; keep examples short.
|
|
40
45
|
schedule={
|
|
41
46
|
"mode": "segments",
|
|
42
47
|
"segments": [
|
|
43
48
|
{
|
|
44
|
-
"
|
|
49
|
+
"duration_s": 2.0,
|
|
50
|
+
"airspeed_target": 0.0,
|
|
51
|
+
"per_group": {"main": {"throttle_target": 70, "throttle_ramp": "linear"}},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"duration_s": 20.0,
|
|
45
55
|
"airspeed_target": 0.0,
|
|
46
56
|
"per_group": {"main": {"throttle_target": 70}},
|
|
47
|
-
}
|
|
57
|
+
},
|
|
48
58
|
],
|
|
49
59
|
},
|
|
50
|
-
#
|
|
51
|
-
termination={"mode": "
|
|
60
|
+
# Fixed-duration mission: run the schedule to its end.
|
|
61
|
+
termination={"mode": "fixed"},
|
|
52
62
|
)
|
|
53
63
|
print(f"created {dyn['id']}, status={dyn['status']}")
|
|
54
64
|
|
|
@@ -29,7 +29,7 @@ project = client.projects.create(name="sdk read-dynamic example")
|
|
|
29
29
|
# Resolve by name, or paste explicit IDs: motor_id = "comp_motor_xxx"
|
|
30
30
|
motor = client.components.find(name="Spektrum Avian 4260 800Kv")
|
|
31
31
|
prop = client.components.find(name="10.5x4.5")
|
|
32
|
-
battery = client.components.find(name="4S
|
|
32
|
+
battery = client.components.find(name="Liperior 5000mAh 4S 35C")
|
|
33
33
|
|
|
34
34
|
dyn = client.dynamic_simulations.create(
|
|
35
35
|
project_id=project["id"],
|
|
@@ -45,18 +45,24 @@ dyn = client.dynamic_simulations.create(
|
|
|
45
45
|
"propeller_component_id": prop["id"],
|
|
46
46
|
}
|
|
47
47
|
],
|
|
48
|
-
#
|
|
48
|
+
# Ramp to 70% over 2 s (soft-start — a throttle step onto a stationary
|
|
49
|
+
# rotor sags the pack below the low-voltage cutoff), then hold for 20 s.
|
|
49
50
|
schedule={
|
|
50
51
|
"mode": "segments",
|
|
51
52
|
"segments": [
|
|
52
53
|
{
|
|
53
|
-
"
|
|
54
|
+
"duration_s": 2.0,
|
|
55
|
+
"airspeed_target": 0.0,
|
|
56
|
+
"per_group": {"main": {"throttle_target": 70, "throttle_ramp": "linear"}},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"duration_s": 20.0,
|
|
54
60
|
"airspeed_target": 0.0,
|
|
55
61
|
"per_group": {"main": {"throttle_target": 70}},
|
|
56
|
-
}
|
|
62
|
+
},
|
|
57
63
|
],
|
|
58
64
|
},
|
|
59
|
-
termination={"mode": "
|
|
65
|
+
termination={"mode": "fixed"},
|
|
60
66
|
)
|
|
61
67
|
result = client.dynamic_simulations.wait(dyn["id"], timeout=600)
|
|
62
68
|
print(f"dynamic status: {result['status']}")
|
|
@@ -42,7 +42,7 @@ project = client.projects.create(name="sdk read-steady-state example")
|
|
|
42
42
|
# Resolve by name, or paste explicit IDs: motor_id = "comp_motor_xxx"
|
|
43
43
|
motor = client.components.find(name="Spektrum Avian 4260 800Kv")
|
|
44
44
|
prop = client.components.find(name="10.5x4.5")
|
|
45
|
-
battery = client.components.find(name="4S
|
|
45
|
+
battery = client.components.find(name="Liperior 5000mAh 4S 35C")
|
|
46
46
|
|
|
47
47
|
# ---- single point ---------------------------------------------------------
|
|
48
48
|
sim = client.simulations.create(
|
|
@@ -23,7 +23,7 @@ for component in islice(client.components.list(type="motor"), 5):
|
|
|
23
23
|
# round-trip the list yourself. Or paste explicit IDs from the catalog.
|
|
24
24
|
motor = client.components.find(name="Spektrum Avian 4260 800Kv")
|
|
25
25
|
prop = client.components.find(name="10.5x4.5")
|
|
26
|
-
battery = client.components.find(name="4S
|
|
26
|
+
battery = client.components.find(name="Liperior 5000mAh 4S 35C")
|
|
27
27
|
|
|
28
28
|
# Run a single-point simulation and wait for the result.
|
|
29
29
|
sim = client.simulations.create(
|
|
@@ -4,9 +4,9 @@ Run it:
|
|
|
4
4
|
export THRUSTLAB_API_KEY=key_... # never hard-code the key
|
|
5
5
|
python examples/simulations/run_sync.py
|
|
6
6
|
|
|
7
|
-
The verification combo below (Spektrum Avian 4260 800 kV + 10.5x4.5 +
|
|
8
|
-
@ 70% throttle, static) converges to ~9.
|
|
9
|
-
component IDs, or resolve them by name with client.components.find(...).
|
|
7
|
+
The verification combo below (Spektrum Avian 4260 800 kV + 10.5x4.5 + Liperior
|
|
8
|
+
4S 5000 mAh @ 70% throttle, static) converges to ~9.78 N / ~8645 rpm. Swap in
|
|
9
|
+
your own component IDs, or resolve them by name with client.components.find(...).
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
from thrustlab import Client
|
|
@@ -20,7 +20,7 @@ project = client.projects.create(name="sdk single-point example")
|
|
|
20
20
|
# motor_id = "comp_motor_xxx"
|
|
21
21
|
motor = client.components.find(name="Spektrum Avian 4260 800Kv")
|
|
22
22
|
prop = client.components.find(name="10.5x4.5")
|
|
23
|
-
battery = client.components.find(name="4S
|
|
23
|
+
battery = client.components.find(name="Liperior 5000mAh 4S 35C")
|
|
24
24
|
|
|
25
25
|
sim = client.simulations.create(
|
|
26
26
|
project_id=project["id"],
|
|
@@ -18,7 +18,7 @@ project = client.projects.create(name="sdk sweep example")
|
|
|
18
18
|
# Resolve by name, or paste explicit IDs: motor_id = "comp_motor_xxx"
|
|
19
19
|
motor = client.components.find(name="Spektrum Avian 4260 800Kv")
|
|
20
20
|
prop = client.components.find(name="10.5x4.5")
|
|
21
|
-
battery = client.components.find(name="4S
|
|
21
|
+
battery = client.components.find(name="Liperior 5000mAh 4S 35C")
|
|
22
22
|
|
|
23
23
|
sweep = client.sweeps.create(
|
|
24
24
|
project_id=project["id"],
|
|
@@ -39,7 +39,7 @@ dev = [
|
|
|
39
39
|
[project.urls]
|
|
40
40
|
Homepage = "https://thrustlab.com"
|
|
41
41
|
Documentation = "https://thrustlab.com/docs"
|
|
42
|
-
Repository = "https://github.com
|
|
42
|
+
Repository = "https://github.com/kbedrich/thrustlab-sdk"
|
|
43
43
|
Changelog = "https://thrustlab.com/docs/changelog"
|
|
44
44
|
|
|
45
45
|
[tool.hatch.version]
|
|
@@ -149,12 +149,13 @@ if [ -n "$ADVISORY" ]; then
|
|
|
149
149
|
fi
|
|
150
150
|
|
|
151
151
|
# --- 6. fresh git repo: single squashed commit, NO remote, NO push (D-23) -----
|
|
152
|
+
SDK_VERSION="$(sed -n 's/^__version__ = "\(.*\)"/\1/p' "$SRC/thrustlab/_version.py")"
|
|
152
153
|
(
|
|
153
154
|
cd "$TARGET"
|
|
154
155
|
git init -b main -q
|
|
155
156
|
git add -A
|
|
156
|
-
git -c user.name="
|
|
157
|
-
commit -q -m "Initial public release: ThrustLab Python SDK
|
|
157
|
+
git -c user.name="kbedrich" -c user.email="kyle@bedrich.net" \
|
|
158
|
+
commit -q -m "Initial public release: ThrustLab Python SDK v${SDK_VERSION}"
|
|
158
159
|
)
|
|
159
160
|
|
|
160
161
|
# --- post-build assertions ----------------------------------------------------
|
|
@@ -10,10 +10,12 @@ def test_list_passes_params(mock_router, monkeypatch):
|
|
|
10
10
|
route = mock_router.get("/v1/projects").mock(
|
|
11
11
|
return_value=Response(200, json={"data": [], "has_more": False})
|
|
12
12
|
)
|
|
13
|
-
client.projects.list(limit=10,
|
|
13
|
+
client.projects.list(limit=10, cursor="proj_5").data # trigger fetch
|
|
14
14
|
qs = dict(route.calls[0].request.url.params)
|
|
15
15
|
assert qs["limit"] == "10"
|
|
16
|
-
|
|
16
|
+
# The endpoint reads `cursor` — the pre-0.3.2 `starting_after` was ignored.
|
|
17
|
+
assert qs["cursor"] == "proj_5"
|
|
18
|
+
assert "starting_after" not in qs
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
def test_create_calls_post(mock_router, monkeypatch):
|
|
@@ -13,7 +13,10 @@ def test_list_passes_project_filter(mock_router, monkeypatch):
|
|
|
13
13
|
)
|
|
14
14
|
client.simulations.list(project_id="proj_1", limit=5).data # trigger fetch
|
|
15
15
|
qs = dict(route.calls[0].request.url.params)
|
|
16
|
-
|
|
16
|
+
# The endpoint's filter param is `project` — the pre-0.3.2 `project_id`
|
|
17
|
+
# key was silently ignored server-side (filter did nothing).
|
|
18
|
+
assert qs["project"] == "proj_1"
|
|
19
|
+
assert "project_id" not in qs
|
|
17
20
|
assert qs["limit"] == "5"
|
|
18
21
|
|
|
19
22
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from httpx import Response
|
|
2
|
+
|
|
3
|
+
from thrustlab import Client
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_list_calls_get(mock_router, monkeypatch):
|
|
7
|
+
monkeypatch.setenv("THRUSTLAB_API_KEY", "key_test")
|
|
8
|
+
monkeypatch.setenv("THRUSTLAB_BASE_URL", "https://api.test.local")
|
|
9
|
+
client = Client()
|
|
10
|
+
route = mock_router.get("/v1/starred_components").mock(
|
|
11
|
+
return_value=Response(200, json={"data": [], "has_more": False})
|
|
12
|
+
)
|
|
13
|
+
client.starred_components.list(project_id="proj_1").data # trigger fetch
|
|
14
|
+
assert route.call_count == 1
|
|
15
|
+
qs = dict(route.calls[0].request.url.params)
|
|
16
|
+
# The endpoint's filter param is `project` (the SDK kwarg stays project_id).
|
|
17
|
+
assert qs["project"] == "proj_1"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_list_passes_component_type_and_cursor(mock_router, monkeypatch):
|
|
21
|
+
monkeypatch.setenv("THRUSTLAB_API_KEY", "key_test")
|
|
22
|
+
monkeypatch.setenv("THRUSTLAB_BASE_URL", "https://api.test.local")
|
|
23
|
+
client = Client()
|
|
24
|
+
route = mock_router.get("/v1/starred_components").mock(
|
|
25
|
+
return_value=Response(200, json={"data": [], "has_more": False})
|
|
26
|
+
)
|
|
27
|
+
client.starred_components.list(
|
|
28
|
+
component_type="motor", cursor="star_abc"
|
|
29
|
+
).data
|
|
30
|
+
qs = dict(route.calls[0].request.url.params)
|
|
31
|
+
assert qs["component_type"] == "motor"
|
|
32
|
+
assert qs["cursor"] == "star_abc"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_add_calls_post(mock_router, monkeypatch):
|
|
36
|
+
monkeypatch.setenv("THRUSTLAB_API_KEY", "key_test")
|
|
37
|
+
monkeypatch.setenv("THRUSTLAB_BASE_URL", "https://api.test.local")
|
|
38
|
+
client = Client()
|
|
39
|
+
route = mock_router.post("/v1/starred_components").mock(
|
|
40
|
+
return_value=Response(
|
|
41
|
+
201,
|
|
42
|
+
json={
|
|
43
|
+
"id": "star_1",
|
|
44
|
+
"project_id": "proj_1",
|
|
45
|
+
"component_id": "cmp_1",
|
|
46
|
+
"component_type": "motor",
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
out = client.starred_components.add(
|
|
51
|
+
project_id="proj_1", component_id="cmp_1", component_type="motor"
|
|
52
|
+
)
|
|
53
|
+
assert out["id"] == "star_1"
|
|
54
|
+
assert route.calls[0].request.headers["Idempotency-Key"]
|
|
55
|
+
import json
|
|
56
|
+
|
|
57
|
+
body = json.loads(route.calls[0].request.content)
|
|
58
|
+
assert body == {
|
|
59
|
+
"project_id": "proj_1",
|
|
60
|
+
"component_id": "cmp_1",
|
|
61
|
+
"component_type": "motor",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_remove_calls_delete(mock_router, monkeypatch):
|
|
66
|
+
monkeypatch.setenv("THRUSTLAB_API_KEY", "key_test")
|
|
67
|
+
monkeypatch.setenv("THRUSTLAB_BASE_URL", "https://api.test.local")
|
|
68
|
+
client = Client()
|
|
69
|
+
route = mock_router.delete("/v1/starred_components/star_1").mock(
|
|
70
|
+
return_value=Response(204)
|
|
71
|
+
)
|
|
72
|
+
# Removal keys on the star's own id, not the component id.
|
|
73
|
+
client.starred_components.remove("star_1")
|
|
74
|
+
assert route.call_count == 1
|
|
@@ -13,7 +13,10 @@ def test_list_passes_project_filter(mock_router, monkeypatch):
|
|
|
13
13
|
)
|
|
14
14
|
client.sweeps.list(project_id="proj_1").data # trigger fetch
|
|
15
15
|
qs = dict(route.calls[0].request.url.params)
|
|
16
|
-
|
|
16
|
+
# The endpoint's filter param is `project` — the pre-0.3.2 `project_id`
|
|
17
|
+
# key was silently ignored server-side (filter did nothing).
|
|
18
|
+
assert qs["project"] == "proj_1"
|
|
19
|
+
assert "project_id" not in qs
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
def test_create_returns_pending(mock_router, monkeypatch):
|
|
@@ -119,14 +119,15 @@ class Transport:
|
|
|
119
119
|
return None
|
|
120
120
|
return resp.json()
|
|
121
121
|
|
|
122
|
-
# Error path. Parse envelope: { "error": { "type": ..., "code": ..., "message": ..., "param": ... } }.
|
|
123
|
-
request_id = resp.headers.get("X-Request-ID")
|
|
122
|
+
# Error path. Parse envelope: { "error": { "type": ..., "code": ..., "message": ..., "param": ..., "request_id": ... } }.
|
|
124
123
|
body: dict[str, Any] = {}
|
|
125
124
|
try:
|
|
126
125
|
body = resp.json()
|
|
127
126
|
except ValueError:
|
|
128
127
|
pass
|
|
129
128
|
env = body.get("error", {}) if isinstance(body, dict) else {}
|
|
129
|
+
# The envelope's request_id is authoritative; the header is a fallback.
|
|
130
|
+
request_id = env.get("request_id") or resp.headers.get("X-Request-ID")
|
|
130
131
|
|
|
131
132
|
kwargs = dict(
|
|
132
133
|
code=env.get("code"),
|
|
@@ -44,7 +44,9 @@ class DynamicSimulationsResource(Resource):
|
|
|
44
44
|
"""
|
|
45
45
|
params: dict[str, Any] = {"limit": limit}
|
|
46
46
|
if project_id:
|
|
47
|
-
|
|
47
|
+
# The endpoint's filter param is ``project`` (pre-0.3.2 the SDK
|
|
48
|
+
# sent an ignored ``project_id`` key — the filter did nothing).
|
|
49
|
+
params["project"] = project_id
|
|
48
50
|
if cursor:
|
|
49
51
|
params["cursor"] = cursor
|
|
50
52
|
return CursorPager(
|
|
@@ -17,16 +17,21 @@ from thrustlab.resources._base import Resource
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class ProjectsResource(Resource):
|
|
20
|
-
def list(self, *, limit: int = 20,
|
|
20
|
+
def list(self, *, limit: int = 20, cursor: Optional[str] = None) -> CursorPager[dict[str, Any]]:
|
|
21
21
|
"""List projects (cursor-paginated).
|
|
22
22
|
|
|
23
|
+
Args:
|
|
24
|
+
cursor: Opaque pagination token from a prior response's
|
|
25
|
+
``next_cursor`` (the backend reads ``cursor``; the pre-0.3.2
|
|
26
|
+
``starting_after`` kwarg was silently ignored server-side).
|
|
27
|
+
|
|
23
28
|
Returns a :class:`CursorPager` that lazily fetches subsequent pages.
|
|
24
29
|
Iterate to consume all pages, or access ``.data`` / ``.has_more`` for
|
|
25
30
|
manual cursor control.
|
|
26
31
|
"""
|
|
27
32
|
params: dict[str, Any] = {"limit": limit}
|
|
28
|
-
if
|
|
29
|
-
params["
|
|
33
|
+
if cursor:
|
|
34
|
+
params["cursor"] = cursor
|
|
30
35
|
return CursorPager(
|
|
31
36
|
fetch_page=lambda p: self._transport.request("GET", "/v1/projects", params=p),
|
|
32
37
|
initial_params=params,
|
|
@@ -21,12 +21,18 @@ class SimulationsResource(Resource):
|
|
|
21
21
|
self,
|
|
22
22
|
*,
|
|
23
23
|
project_id: Optional[str] = None,
|
|
24
|
+
status: Optional[str] = None,
|
|
24
25
|
limit: int = 20,
|
|
25
26
|
cursor: Optional[str] = None,
|
|
26
27
|
) -> CursorPager[dict[str, Any]]:
|
|
27
28
|
"""List simulations (cursor-paginated).
|
|
28
29
|
|
|
29
30
|
Args:
|
|
31
|
+
project_id: Optional project filter (sent as the endpoint's
|
|
32
|
+
``project`` query param; pre-0.3.2 the SDK sent an ignored
|
|
33
|
+
``project_id`` key, so this filter silently did nothing).
|
|
34
|
+
status: Optional status filter (``queued`` / ``running`` /
|
|
35
|
+
``completed`` / ``failed`` / ``canceled``).
|
|
30
36
|
cursor: Opaque pagination token from a prior response's
|
|
31
37
|
``next_cursor`` (the ``/v1/simulations`` endpoint reads
|
|
32
38
|
``cursor``). Omit to start from the first page.
|
|
@@ -35,7 +41,9 @@ class SimulationsResource(Resource):
|
|
|
35
41
|
"""
|
|
36
42
|
params: dict[str, Any] = {"limit": limit}
|
|
37
43
|
if project_id:
|
|
38
|
-
params["
|
|
44
|
+
params["project"] = project_id
|
|
45
|
+
if status:
|
|
46
|
+
params["status"] = status
|
|
39
47
|
if cursor:
|
|
40
48
|
params["cursor"] = cursor
|
|
41
49
|
return CursorPager(
|