temperkb-py 0.5.1__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.
- temperkb_py-0.5.1/.gitignore +10 -0
- temperkb_py-0.5.1/LICENSE +21 -0
- temperkb_py-0.5.1/PKG-INFO +250 -0
- temperkb_py-0.5.1/README.md +232 -0
- temperkb_py-0.5.1/pyproject.toml +130 -0
- temperkb_py-0.5.1/temper/__init__.py +93 -0
- temperkb_py-0.5.1/temper/_validate.py +147 -0
- temperkb_py-0.5.1/temper/act.py +59 -0
- temperkb_py-0.5.1/temper/client.py +137 -0
- temperkb_py-0.5.1/temper/connection.py +353 -0
- temperkb_py-0.5.1/temper/credentials.py +359 -0
- temperkb_py-0.5.1/temper/errors.py +243 -0
- temperkb_py-0.5.1/temper/generated/__init__.py +770 -0
- temperkb_py-0.5.1/temper/generated/api/__init__.py +29 -0
- temperkb_py-0.5.1/temper/generated/api/access_api.py +1420 -0
- temperkb_py-0.5.1/temper/generated/api/auditor_api.py +888 -0
- temperkb_py-0.5.1/temper/generated/api/blobs_api.py +3134 -0
- temperkb_py-0.5.1/temper/generated/api/cognitive_maps_api.py +3980 -0
- temperkb_py-0.5.1/temper/generated/api/contexts_api.py +4146 -0
- temperkb_py-0.5.1/temper/generated/api/data_artifact_shapes_api.py +914 -0
- temperkb_py-0.5.1/temper/generated/api/data_artifacts_api.py +998 -0
- temperkb_py-0.5.1/temper/generated/api/events_api.py +607 -0
- temperkb_py-0.5.1/temper/generated/api/facets_api.py +1627 -0
- temperkb_py-0.5.1/temper/generated/api/graph_api.py +2506 -0
- temperkb_py-0.5.1/temper/generated/api/health_api.py +295 -0
- temperkb_py-0.5.1/temper/generated/api/ingest_api.py +1526 -0
- temperkb_py-0.5.1/temper/generated/api/invitations_api.py +2036 -0
- temperkb_py-0.5.1/temper/generated/api/invocations_api.py +1200 -0
- temperkb_py-0.5.1/temper/generated/api/meta_api.py +629 -0
- temperkb_py-0.5.1/temper/generated/api/profile_api.py +852 -0
- temperkb_py-0.5.1/temper/generated/api/query_api.py +337 -0
- temperkb_py-0.5.1/temper/generated/api/reblocking_api.py +337 -0
- temperkb_py-0.5.1/temper/generated/api/relationships_api.py +1280 -0
- temperkb_py-0.5.1/temper/generated/api/resources_api.py +5430 -0
- temperkb_py-0.5.1/temper/generated/api/schema_api.py +847 -0
- temperkb_py-0.5.1/temper/generated/api/search_api.py +334 -0
- temperkb_py-0.5.1/temper/generated/api/slack_link_api.py +598 -0
- temperkb_py-0.5.1/temper/generated/api/steward_api.py +1463 -0
- temperkb_py-0.5.1/temper/generated/api/teams_api.py +2692 -0
- temperkb_py-0.5.1/temper/generated/api_client.py +804 -0
- temperkb_py-0.5.1/temper/generated/api_response.py +21 -0
- temperkb_py-0.5.1/temper/generated/configuration.py +606 -0
- temperkb_py-0.5.1/temper/generated/exceptions.py +218 -0
- temperkb_py-0.5.1/temper/generated/models/__init__.py +353 -0
- temperkb_py-0.5.1/temper/generated/models/accept_invitation_response.py +94 -0
- temperkb_py-0.5.1/temper/generated/models/act_input.py +137 -0
- temperkb_py-0.5.1/temper/generated/models/act_invocation.py +160 -0
- temperkb_py-0.5.1/temper/generated/models/act_name.py +262 -0
- temperkb_py-0.5.1/temper/generated/models/act_quantity.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/act_refusal.py +94 -0
- temperkb_py-0.5.1/temper/generated/models/actor_authority.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/add_member_request.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/advance_watermark_ack.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/advance_watermark_request.py +101 -0
- temperkb_py-0.5.1/temper/generated/models/agent_authorship.py +117 -0
- temperkb_py-0.5.1/temper/generated/models/anchor_address_resolution.py +151 -0
- temperkb_py-0.5.1/temper/generated/models/anchor_address_resolution_one_of.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/anchor_address_resolution_one_of1.py +104 -0
- temperkb_py-0.5.1/temper/generated/models/anchor_address_resolution_one_of2.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/anchor_shape.py +114 -0
- temperkb_py-0.5.1/temper/generated/models/anchor_verdict.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/append_block_payload.py +110 -0
- temperkb_py-0.5.1/temper/generated/models/artifact_commit_request.py +163 -0
- temperkb_py-0.5.1/temper/generated/models/artifact_commit_response.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/artifact_view.py +119 -0
- temperkb_py-0.5.1/temper/generated/models/assert_relationship_request.py +154 -0
- temperkb_py-0.5.1/temper/generated/models/atlas_edge.py +108 -0
- temperkb_py-0.5.1/temper/generated/models/atlas_entry.py +112 -0
- temperkb_py-0.5.1/temper/generated/models/atlas_home.py +106 -0
- temperkb_py-0.5.1/temper/generated/models/atlas_node.py +139 -0
- temperkb_py-0.5.1/temper/generated/models/atlas_subgraph.py +106 -0
- temperkb_py-0.5.1/temper/generated/models/audit_citation.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/audit_sweep_row.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/auditor_dispatch_tick_request.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/auditor_dispatch_tick_response.py +104 -0
- temperkb_py-0.5.1/temper/generated/models/auditor_job_complete_ack.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/bind_team_outcome.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/bind_team_request.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/blob_commit_response.py +104 -0
- temperkb_py-0.5.1/temper/generated/models/blob_delete_ack.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/blob_relation_ack.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/blob_relation_assert_request.py +154 -0
- temperkb_py-0.5.1/temper/generated/models/blob_relation_direction.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/blob_relation_edge_direction.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/blob_relation_row.py +121 -0
- temperkb_py-0.5.1/temper/generated/models/blob_summary.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/blob_upload_begin_request.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/blob_upload_begin_response.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/blob_upload_finalize_request.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/blob_upload_progress.py +101 -0
- temperkb_py-0.5.1/temper/generated/models/blob_upload_segment_info.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/block_chunk_ref.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/block_fold_disposition.py +151 -0
- temperkb_py-0.5.1/temper/generated/models/block_fold_disposition_one_of.py +114 -0
- temperkb_py-0.5.1/temper/generated/models/block_fold_disposition_one_of1.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/block_fold_disposition_one_of2.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/block_provenance_row.py +111 -0
- temperkb_py-0.5.1/temper/generated/models/block_read.py +151 -0
- temperkb_py-0.5.1/temper/generated/models/block_read_one_of.py +127 -0
- temperkb_py-0.5.1/temper/generated/models/block_read_one_of1.py +116 -0
- temperkb_py-0.5.1/temper/generated/models/block_read_one_of2.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/block_successor.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/blocks_response.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/body_storage.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/bridge.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/bulk_reassign_ack.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/bulk_reassign_request.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/change_role_request.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/charter_block.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/charter_disposition.py +39 -0
- temperkb_py-0.5.1/temper/generated/models/citation_audit_request.py +104 -0
- temperkb_py-0.5.1/temper/generated/models/citation_audit_row.py +111 -0
- temperkb_py-0.5.1/temper/generated/models/claimed_audit_job.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/claimed_job.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/close_invocation_request.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_analytics_row.py +105 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_detail.py +112 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_foundation_row.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_grant_body.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_region_metrics_row.py +126 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_region_row.py +109 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_regulation_row.py +100 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_revoke_body.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_row.py +108 -0
- temperkb_py-0.5.1/temper/generated/models/cogmap_staleness.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/combine_node.py +104 -0
- temperkb_py-0.5.1/temper/generated/models/combine_op.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/composition.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/composition_trace.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/confidence_band.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/content_response.py +109 -0
- temperkb_py-0.5.1/temper/generated/models/context_create_request.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/context_owner_ref.py +154 -0
- temperkb_py-0.5.1/temper/generated/models/context_owner_ref_one_of.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/context_owner_ref_one_of1.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/context_panorama.py +112 -0
- temperkb_py-0.5.1/temper/generated/models/context_row.py +108 -0
- temperkb_py-0.5.1/temper/generated/models/context_row_with_counts.py +110 -0
- temperkb_py-0.5.1/temper/generated/models/create_cogmap_outcome.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/create_cogmap_request.py +116 -0
- temperkb_py-0.5.1/temper/generated/models/create_invitation_request.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/create_request_body.py +102 -0
- temperkb_py-0.5.1/temper/generated/models/create_review_body.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/deactivated.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/delete_response.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/denied.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/device_overrides.py +108 -0
- temperkb_py-0.5.1/temper/generated/models/discouraged_open_meta_key.py +90 -0
- temperkb_py-0.5.1/temper/generated/models/dispatch_tick_request.py +100 -0
- temperkb_py-0.5.1/temper/generated/models/dispatch_tick_response.py +104 -0
- temperkb_py-0.5.1/temper/generated/models/disposition.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/doc_type_description.py +106 -0
- temperkb_py-0.5.1/temper/generated/models/doc_type_summary.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/drift_sweep_row.py +102 -0
- temperkb_py-0.5.1/temper/generated/models/edge_facet_row.py +142 -0
- temperkb_py-0.5.1/temper/generated/models/edge_facet_set_request.py +148 -0
- temperkb_py-0.5.1/temper/generated/models/edge_facets_response.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/edge_filter.py +102 -0
- temperkb_py-0.5.1/temper/generated/models/edge_kind.py +39 -0
- temperkb_py-0.5.1/temper/generated/models/element_event.py +118 -0
- temperkb_py-0.5.1/temper/generated/models/element_kind.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/embed_dispatch_summary.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/enforcement_mode.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/entitlements.py +106 -0
- temperkb_py-0.5.1/temper/generated/models/entry_bounds.py +94 -0
- temperkb_py-0.5.1/temper/generated/models/error_body.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/error_detail.py +101 -0
- temperkb_py-0.5.1/temper/generated/models/error_details.py +137 -0
- temperkb_py-0.5.1/temper/generated/models/event_cursor_response.py +94 -0
- temperkb_py-0.5.1/temper/generated/models/event_trail.py +102 -0
- temperkb_py-0.5.1/temper/generated/models/exact_arm.py +106 -0
- temperkb_py-0.5.1/temper/generated/models/exact_hit.py +94 -0
- temperkb_py-0.5.1/temper/generated/models/extent.py +151 -0
- temperkb_py-0.5.1/temper/generated/models/extent_one_of.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/extent_one_of1.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/extent_one_of2.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/facet_ack.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/facet_predicate.py +90 -0
- temperkb_py-0.5.1/temper/generated/models/facet_retract_ack.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/facet_set_request.py +143 -0
- temperkb_py-0.5.1/temper/generated/models/finalize_payload.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/fold_relationship_request.py +144 -0
- temperkb_py-0.5.1/temper/generated/models/grant_outcome.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/graph_edge_row.py +123 -0
- temperkb_py-0.5.1/temper/generated/models/group_key_meta.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/health_response.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/home_cogmap.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/home_context.py +105 -0
- temperkb_py-0.5.1/temper/generated/models/id_kind.py +194 -0
- temperkb_py-0.5.1/temper/generated/models/id_provenance.py +137 -0
- temperkb_py-0.5.1/temper/generated/models/id_provenance_one_of.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/id_provenance_one_of1.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/id_set.py +107 -0
- temperkb_py-0.5.1/temper/generated/models/idp_revocation.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/illegal_transition.py +105 -0
- temperkb_py-0.5.1/temper/generated/models/ingest_create_response.py +137 -0
- temperkb_py-0.5.1/temper/generated/models/ingest_delta.py +120 -0
- temperkb_py-0.5.1/temper/generated/models/ingest_payload.py +224 -0
- temperkb_py-0.5.1/temper/generated/models/ingest_state.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/inherited_read_grant.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/inherited_share.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/input_source.py +151 -0
- temperkb_py-0.5.1/temper/generated/models/input_source_one_of.py +108 -0
- temperkb_py-0.5.1/temper/generated/models/input_source_one_of1.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/input_source_one_of2.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/insufficient_authority.py +100 -0
- temperkb_py-0.5.1/temper/generated/models/intention.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/invitation_status.py +39 -0
- temperkb_py-0.5.1/temper/generated/models/invitation_token_request.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/invitee_invitation.py +112 -0
- temperkb_py-0.5.1/temper/generated/models/invocation_ack.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/invocation_act_row.py +114 -0
- temperkb_py-0.5.1/temper/generated/models/invocation_close_ack.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/invocation_summary.py +112 -0
- temperkb_py-0.5.1/temper/generated/models/invocation_view.py +148 -0
- temperkb_py-0.5.1/temper/generated/models/join_request.py +145 -0
- temperkb_py-0.5.1/temper/generated/models/join_request_status.py +39 -0
- temperkb_py-0.5.1/temper/generated/models/kind_owner_input.py +137 -0
- temperkb_py-0.5.1/temper/generated/models/kind_owner_input_one_of.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/kind_owner_input_one_of1.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/lineage_node.py +101 -0
- temperkb_py-0.5.1/temper/generated/models/managed_meta.py +156 -0
- temperkb_py-0.5.1/temper/generated/models/match_location.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/materialize_ack.py +118 -0
- temperkb_py-0.5.1/temper/generated/models/materialize_delta.py +111 -0
- temperkb_py-0.5.1/temper/generated/models/materialize_request.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/merge_policy.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/meta_update_payload.py +156 -0
- temperkb_py-0.5.1/temper/generated/models/narrowed_by.py +94 -0
- temperkb_py-0.5.1/temper/generated/models/no_prior_standing.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/no_standing.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/node_home.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/open_invocation_request.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/open_meta_convention.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/ord_op.py +39 -0
- temperkb_py-0.5.1/temper/generated/models/orphan_node.py +109 -0
- temperkb_py-0.5.1/temper/generated/models/outcome_declaration.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/pending_invitation_counts.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/plan_refusal.py +115 -0
- temperkb_py-0.5.1/temper/generated/models/plan_refusal_details.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/polarity.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/profile.py +126 -0
- temperkb_py-0.5.1/temper/generated/models/profile_auth_link.py +109 -0
- temperkb_py-0.5.1/temper/generated/models/profile_update_request.py +111 -0
- temperkb_py-0.5.1/temper/generated/models/profile_with_entitlements.py +122 -0
- temperkb_py-0.5.1/temper/generated/models/property_op.py +151 -0
- temperkb_py-0.5.1/temper/generated/models/property_op_one_of.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/property_op_one_of1.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/property_op_one_of2.py +105 -0
- temperkb_py-0.5.1/temper/generated/models/property_predicate.py +94 -0
- temperkb_py-0.5.1/temper/generated/models/provenance_source.py +151 -0
- temperkb_py-0.5.1/temper/generated/models/provenance_source_one_of.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/provenance_source_one_of1.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/provenance_source_one_of2.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/public_system_settings.py +107 -0
- temperkb_py-0.5.1/temper/generated/models/quantity_scale.py +151 -0
- temperkb_py-0.5.1/temper/generated/models/quantity_scale_one_of.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/quantity_scale_one_of1.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/quantity_scale_one_of2.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/query_response.py +107 -0
- temperkb_py-0.5.1/temper/generated/models/reassign_ack.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/reassign_context_outcome.py +113 -0
- temperkb_py-0.5.1/temper/generated/models/reassign_context_request.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/reassign_resource_request.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_candidate.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome.py +230 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome_one_of.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome_one_of1.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome_one_of1_in_progress.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome_one_of2.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome_one_of3.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome_one_of4.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome_one_of4_error.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_outcome_one_of_reblocked.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_receipt.py +114 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_request.py +109 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_scope.py +154 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_scope_one_of.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_scope_one_of1.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/reblock_summary.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/reconcile_cogmap_request.py +127 -0
- temperkb_py-0.5.1/temper/generated/models/reconcile_edge.py +102 -0
- temperkb_py-0.5.1/temper/generated/models/reconcile_edge_tombstone.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/reconcile_entry.py +116 -0
- temperkb_py-0.5.1/temper/generated/models/reconcile_outcome.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/reconcile_telos.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/reconcile_telos_block.py +90 -0
- temperkb_py-0.5.1/temper/generated/models/reconcile_tombstone.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/refusal.py +235 -0
- temperkb_py-0.5.1/temper/generated/models/refusal_reason.py +670 -0
- temperkb_py-0.5.1/temper/generated/models/region_disclosure.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/region_hit.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/relationship_ack.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/relationship_target.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/remove_member_outcome.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/rename_context_outcome.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/rename_context_request.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/requested.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/residual_bucket.py +90 -0
- temperkb_py-0.5.1/temper/generated/models/residual_context.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/residual_groups.py +98 -0
- temperkb_py-0.5.1/temper/generated/models/residual_owned_reach.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/resource_annotate_request.py +154 -0
- temperkb_py-0.5.1/temper/generated/models/resource_create_request.py +152 -0
- temperkb_py-0.5.1/temper/generated/models/resource_facet_row.py +126 -0
- temperkb_py-0.5.1/temper/generated/models/resource_facets.py +92 -0
- temperkb_py-0.5.1/temper/generated/models/resource_facets_response.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/resource_filter.py +139 -0
- temperkb_py-0.5.1/temper/generated/models/resource_grant_body.py +99 -0
- temperkb_py-0.5.1/temper/generated/models/resource_hit.py +119 -0
- temperkb_py-0.5.1/temper/generated/models/resource_lineage.py +108 -0
- temperkb_py-0.5.1/temper/generated/models/resource_list_response.py +117 -0
- temperkb_py-0.5.1/temper/generated/models/resource_revoke_body.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/resource_section.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/resource_sort_field.py +42 -0
- temperkb_py-0.5.1/temper/generated/models/resource_update_request.py +235 -0
- temperkb_py-0.5.1/temper/generated/models/resource_view.py +202 -0
- temperkb_py-0.5.1/temper/generated/models/restore_context_outcome.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/retire_context_outcome.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/return_spec.py +102 -0
- temperkb_py-0.5.1/temper/generated/models/retype_relationship_request.py +143 -0
- temperkb_py-0.5.1/temper/generated/models/revoke_outcome.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/revoked.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/reweight_relationship_request.py +139 -0
- temperkb_py-0.5.1/temper/generated/models/score_kind.py +194 -0
- temperkb_py-0.5.1/temper/generated/models/scoring.py +94 -0
- temperkb_py-0.5.1/temper/generated/models/search_params.py +152 -0
- temperkb_py-0.5.1/temper/generated/models/search_reason.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/search_response.py +104 -0
- temperkb_py-0.5.1/temper/generated/models/search_result_row.py +115 -0
- temperkb_py-0.5.1/temper/generated/models/search_scope.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/search_scope_info.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/segment_info.py +91 -0
- temperkb_py-0.5.1/temper/generated/models/segmented_begin.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/segmented_begin_response.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/shape_declare_request.py +160 -0
- temperkb_py-0.5.1/temper/generated/models/shape_emptiness.py +39 -0
- temperkb_py-0.5.1/temper/generated/models/shape_view.py +116 -0
- temperkb_py-0.5.1/temper/generated/models/share_context_outcome.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/share_context_request.py +89 -0
- temperkb_py-0.5.1/temper/generated/models/slack_disconnect_request.py +88 -0
- temperkb_py-0.5.1/temper/generated/models/slack_disconnect_response.py +96 -0
- temperkb_py-0.5.1/temper/generated/models/slack_disconnected_principal.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/slice_request.py +95 -0
- temperkb_py-0.5.1/temper/generated/models/sort_order.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/stage_disposition.py +39 -0
- temperkb_py-0.5.1/temper/generated/models/stage_input.py +137 -0
- temperkb_py-0.5.1/temper/generated/models/stage_input_one_of.py +104 -0
- temperkb_py-0.5.1/temper/generated/models/stage_input_one_of1.py +111 -0
- temperkb_py-0.5.1/temper/generated/models/stage_input_trace.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/stage_node.py +137 -0
- temperkb_py-0.5.1/temper/generated/models/stage_output.py +137 -0
- temperkb_py-0.5.1/temper/generated/models/stage_output_one_of.py +105 -0
- temperkb_py-0.5.1/temper/generated/models/stage_output_one_of1.py +105 -0
- temperkb_py-0.5.1/temper/generated/models/stage_relation.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/stage_result.py +162 -0
- temperkb_py-0.5.1/temper/generated/models/stage_trace.py +163 -0
- temperkb_py-0.5.1/temper/generated/models/standing.py +40 -0
- temperkb_py-0.5.1/temper/generated/models/standing_shape.py +103 -0
- temperkb_py-0.5.1/temper/generated/models/subscription.py +118 -0
- temperkb_py-0.5.1/temper/generated/models/subscription_override.py +108 -0
- temperkb_py-0.5.1/temper/generated/models/system_access_details.py +120 -0
- temperkb_py-0.5.1/temper/generated/models/team_create_request.py +110 -0
- temperkb_py-0.5.1/temper/generated/models/team_detail.py +121 -0
- temperkb_py-0.5.1/temper/generated/models/team_invitation.py +115 -0
- temperkb_py-0.5.1/temper/generated/models/team_member_detail.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/team_member_row.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/team_member_source.py +37 -0
- temperkb_py-0.5.1/temper/generated/models/team_role.py +39 -0
- temperkb_py-0.5.1/temper/generated/models/team_row.py +111 -0
- temperkb_py-0.5.1/temper/generated/models/team_update_request.py +100 -0
- temperkb_py-0.5.1/temper/generated/models/territory.py +117 -0
- temperkb_py-0.5.1/temper/generated/models/territory_kind.py +38 -0
- temperkb_py-0.5.1/temper/generated/models/territory_overview.py +116 -0
- temperkb_py-0.5.1/temper/generated/models/unbind_team_outcome.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/unrecognized_standing.py +97 -0
- temperkb_py-0.5.1/temper/generated/models/unshare_context_outcome.py +93 -0
- temperkb_py-0.5.1/temper/generated/models/vault_config.py +118 -0
- temperkb_py-0.5.1/temper/generated/models/via_entry.py +106 -0
- temperkb_py-0.5.1/temper/generated/models/wide_arm.py +108 -0
- temperkb_py-0.5.1/temper/generated/models/wide_hit.py +94 -0
- temperkb_py-0.5.1/temper/generated/py.typed +0 -0
- temperkb_py-0.5.1/temper/generated/rest.py +263 -0
- temperkb_py-0.5.1/temper/py.typed +0 -0
- temperkb_py-0.5.1/temper/refs.py +42 -0
- temperkb_py-0.5.1/temper/version.py +11 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tasker Systems
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: temperkb-py
|
|
3
|
+
Version: 0.5.1
|
|
4
|
+
Summary: Python SDK for the Temper knowledge-base API
|
|
5
|
+
Project-URL: Homepage, https://github.com/tasker-systems/temper
|
|
6
|
+
Project-URL: Source, https://github.com/tasker-systems/temper/tree/main/clients/temper-py
|
|
7
|
+
Project-URL: Issues, https://github.com/tasker-systems/temper/issues
|
|
8
|
+
Author-email: Pete Taylor <pete.jc.taylor@hey.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: knowledge-base,openapi,sdk,temper
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Requires-Dist: pydantic>=2.11
|
|
14
|
+
Requires-Dist: python-dateutil>=2.8.2
|
|
15
|
+
Requires-Dist: typing-extensions>=4.7.1
|
|
16
|
+
Requires-Dist: urllib3<3.0,>=2.1
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# temper-py
|
|
20
|
+
|
|
21
|
+
The Python client for the [Temper](https://github.com/tasker-systems/temper)
|
|
22
|
+
knowledge-base API. Sibling of `temper-rb` and `temper-ts` — all three are pinned to
|
|
23
|
+
the same OpenAPI contract. Workspace-isolated: this package is not a cargo member and
|
|
24
|
+
not a bun workspace member, so nothing in the repo builds it implicitly.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import temper
|
|
28
|
+
from temper.generated.api.resources_api import ResourcesApi
|
|
29
|
+
|
|
30
|
+
temper.configure(base_url="https://temperkb.io")
|
|
31
|
+
|
|
32
|
+
client = temper.Client(
|
|
33
|
+
temper.ClientCredentials(
|
|
34
|
+
token_url="https://temperkb.io/oauth/token",
|
|
35
|
+
client_id="tmpr_...",
|
|
36
|
+
client_secret="...",
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
client.whoami()
|
|
41
|
+
client.call(lambda api: ResourcesApi(api).get_resource(resource_id), idempotent=True)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## What is generated and what is not
|
|
45
|
+
|
|
46
|
+
`temper/generated/**` is emitted from the repo-root `openapi.json` — itself a product
|
|
47
|
+
of the Axum router — by a pinned `openapi-generator`. **Never hand-edit it.** A new
|
|
48
|
+
field on a response DTO restales it exactly as it restales `openapi.json` itself, the
|
|
49
|
+
`temper-rb` gem, and `temper-ts`'s `schema.ts`:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cargo make openapi # regenerates the spec and all three SDKs
|
|
53
|
+
cargo make openapi-py # just this package
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`cargo make check` runs `openapi-py-drift`, which regenerates and diffs. The
|
|
57
|
+
`test-python` CI job runs the same check and never skips, so a contract change that
|
|
58
|
+
leaves this package behind cannot merge.
|
|
59
|
+
|
|
60
|
+
Everything outside `temper/generated/` is hand-written, and
|
|
61
|
+
`.openapi-generator-ignore` keeps the generator off it.
|
|
62
|
+
|
|
63
|
+
| Module | What it is |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `temper.connection` | One `ApiClient` (one urllib3 pool) per process; the token is call-scoped via a `ContextVar` |
|
|
66
|
+
| `temper.credentials` | `BearerToken` and `ClientCredentials`, pinned to `tests/contracts/m2m-token-request.json` |
|
|
67
|
+
| `temper.errors` | The transient/permanent split, and `map_error` |
|
|
68
|
+
| `temper.client` | `Client.call()` — the one seam carrying retry policy and 401 repair |
|
|
69
|
+
| `temper.act` | `ActInput`'s seven wire keys, with the confidence invariant enforced at construction |
|
|
70
|
+
| `temper.refs` | `parse_ref` — a port of `temper_workflow::operations::parse_ref` |
|
|
71
|
+
| `temper._validate` | The admission checks the seams share: endpoints, and values that become headers |
|
|
72
|
+
|
|
73
|
+
### There are no per-endpoint wrapper methods, deliberately
|
|
74
|
+
|
|
75
|
+
The gem hand-writes `Resources`, `Contexts`, `CognitiveMaps` because a Ruby caller
|
|
76
|
+
otherwise passes an untyped hash. The generated Python core already answers that:
|
|
77
|
+
every operation is a typed method over pydantic models. A hand-written
|
|
78
|
+
`resources.create(...)` would be a second, worse spelling of something already
|
|
79
|
+
correct — and a place for the two to drift. `temper-ts` declines the same wrappers
|
|
80
|
+
for the same reason.
|
|
81
|
+
|
|
82
|
+
What the generated core does *not* answer is which failures are worth retrying and
|
|
83
|
+
who repairs a dead token. That is what `Client.call()` is:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
client.call(fn, idempotent=True) # a safe method: 5xx and transport failures retry
|
|
87
|
+
client.call(fn) # a write: NEVER auto-retried
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
A 401 is repaired once either way — re-authenticating is not re-submitting. A
|
|
91
|
+
`BearerToken` cannot mint, so its 401 comes back untouched rather than being replaced
|
|
92
|
+
by a message about the client's own plumbing.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### What `configure()` will pass through
|
|
96
|
+
|
|
97
|
+
Keyword arguments beyond `base_url` and `device_id` reach the generated
|
|
98
|
+
`Configuration`, and only the ones on an **allowlist** do:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
temper.configure(
|
|
102
|
+
base_url="https://temper.internal",
|
|
103
|
+
ssl_ca_cert="/etc/ssl/private-ca.pem", # trust a private CA
|
|
104
|
+
tls_server_name="temper.internal", # the SNI name the cert carries
|
|
105
|
+
connection_pool_maxsize=32,
|
|
106
|
+
proxy="http://proxy.internal:3128",
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`ssl_ca_cert`, `ca_cert_data`, `cert_file`, `key_file`, `tls_server_name`,
|
|
111
|
+
`connection_pool_maxsize`, `proxy`, `proxy_headers`, `socket_options`,
|
|
112
|
+
`datetime_format`, `date_format` — that is the whole list, and an unrecognised name
|
|
113
|
+
is a `TypeError` rather than a silent passthrough. Three of the arguments it refuses
|
|
114
|
+
are why the list is an allowlist:
|
|
115
|
+
|
|
116
|
+
| Refused | What it would have done |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `debug=True` | Sets `httplib.HTTPConnection.debuglevel = 1`, a **class** attribute — every HTTP request in the process starts printing its request headers to stdout, `Authorization: Bearer …` included. Raise the level on the `temper.generated` or `urllib3` logger instead; neither touches httplib. |
|
|
119
|
+
| `verify_ssl=False` | `ssl.CERT_NONE` on the pool: any certificate from anything that answers, and the bearer token goes to whoever intercepted the connection. Use `ssl_ca_cert` / `ca_cert_data`. |
|
|
120
|
+
| `assert_hostname=False` | Keeps verification on but stops checking the certificate is for the host you dialled. Use `tls_server_name`. |
|
|
121
|
+
|
|
122
|
+
The rest are refused because this module owns them (`host`, `retries`, the
|
|
123
|
+
`server_*` family) or because credentials are call-scoped, not connection-scoped
|
|
124
|
+
(`access_token`, `api_key`, `username`/`password`). Every refusal names its reason,
|
|
125
|
+
and all of them fire at `configure()` — not at the first API call, which is when a
|
|
126
|
+
lazily-built `Configuration` would have raised.
|
|
127
|
+
|
|
128
|
+
### Endpoints, and plaintext http
|
|
129
|
+
|
|
130
|
+
`base_url` and `ClientCredentials(token_url=...)` must be absolute `https` URLs, with
|
|
131
|
+
no userinfo (`https://id:secret@host` puts the secret in every error message that
|
|
132
|
+
names the URL) and no query or fragment. Plaintext `http` is accepted for the
|
|
133
|
+
loopback interface — a test server, a `temper serve` on your laptop — and refused
|
|
134
|
+
anywhere else, because a bearer token and a `client_secret` both travel in the clear
|
|
135
|
+
over it. Where TLS genuinely terminates elsewhere, say so:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
temper.configure(base_url="http://temper.internal", allow_insecure_http=True)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Connections and forking
|
|
142
|
+
|
|
143
|
+
`temper.configure()` installs a process-global connection: one `ApiClient`, one
|
|
144
|
+
urllib3 pool, one TLS handshake amortized across every call. The access token is *not*
|
|
145
|
+
on it — it is bound per call from the `Client`'s credential, through a `ContextVar`,
|
|
146
|
+
so one connection serves every concurrent caller with their own identity.
|
|
147
|
+
|
|
148
|
+
urllib3 has no fork hook (the gem gets one free from `connection_pool >= 2.4`), so a
|
|
149
|
+
forking server must drop the inherited sockets itself:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import os, temper
|
|
153
|
+
|
|
154
|
+
os.register_at_fork(after_in_child=temper.reset_connection)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Errors
|
|
158
|
+
|
|
159
|
+
`map_error` translates the generated core's `ApiException` — and the raw `urllib3`
|
|
160
|
+
errors a transport failure raises — into a tree whose top-level split is the one that
|
|
161
|
+
matters operationally:
|
|
162
|
+
|
|
163
|
+
- `TransientError` → `ServerError`, `RateLimited` (with `retry_after`), `TransportError`.
|
|
164
|
+
Let these escape a job; a retry is what fixes them.
|
|
165
|
+
- `PermanentError` → `Unauthorized`, `Forbidden`, `SystemAccessRequired`, `NotFound`,
|
|
166
|
+
`Conflict`, `BadRequest`. Catch these and dead-letter them.
|
|
167
|
+
|
|
168
|
+
`SystemAccessRequired.refusal` returns the typed refusal the server sent (`Denied`,
|
|
169
|
+
`Revoked`, `IllegalTransition`, …) so a worker can tell "never granted" from "granted
|
|
170
|
+
and then revoked" without matching on a message string. `refusal_kind` gives the raw
|
|
171
|
+
discriminator when this build predates the kind the server named.
|
|
172
|
+
|
|
173
|
+
> The gem calls the transport failure `Temper::ConnectionError`. Here it is
|
|
174
|
+
> `TransportError`, because `ConnectionError` is a Python builtin and shadowing it
|
|
175
|
+
> would make `except ConnectionError` silently catch the wrong thing.
|
|
176
|
+
|
|
177
|
+
### Composition bounds raise BEFORE the request `[2026-08-28]`
|
|
178
|
+
|
|
179
|
+
`/api/query`'s contract publishes ceilings on what one composition may declare, and the
|
|
180
|
+
generated pydantic models enforce them locally — so these surface as a `ValidationError`
|
|
181
|
+
at construction (and, since `validate_assignment` is on, at mutation) rather than as a
|
|
182
|
+
`BadRequest` carrying a typed refusal:
|
|
183
|
+
|
|
184
|
+
| field | ceiling |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `Composition.stages` | 64 |
|
|
187
|
+
| `Intention.query` | 4096 |
|
|
188
|
+
| `IdSet.ids` | 256 |
|
|
189
|
+
| `ResourceFilter.doc_type` / `.tags`, `EdgeFilter.labels` | 256 |
|
|
190
|
+
|
|
191
|
+
**This is a behaviour change for code that already builds large plans**: a 300-id `IdSet`
|
|
192
|
+
used to construct fine and reach the server. It now raises before any HTTP call.
|
|
193
|
+
|
|
194
|
+
**The client counts characters; the server counts bytes.** A 4096-character CJK question is
|
|
195
|
+
8192 bytes — it constructs cleanly here and is refused server-side as `intention_too_long`.
|
|
196
|
+
The skew is one-directional by construction (a UTF-8 string is never fewer bytes than
|
|
197
|
+
characters), so the client can only ever under-enforce, never refuse something the server
|
|
198
|
+
would have run.
|
|
199
|
+
|
|
200
|
+
Two ceilings are deliberately NOT enforced here, because neither is a contract fact: the
|
|
201
|
+
per-stage predicate and probe caps, and the aggregate embed budget
|
|
202
|
+
(`intention_budget_exceeded`) — what a deployment can embed in one request is a property of
|
|
203
|
+
that deployment. Those arrive as refusals.
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
## Credentials
|
|
207
|
+
|
|
208
|
+
`BearerToken(token)` and `ClientCredentials(...)` check their inputs at construction,
|
|
209
|
+
because the two ways a credential arrives wrong both surface as an unexplained
|
|
210
|
+
`invalid_client` or 401 hours later:
|
|
211
|
+
|
|
212
|
+
- **Whitespace.** `TEMPER_M2M_CLIENT_SECRET=$(cat secret.txt)` keeps the trailing
|
|
213
|
+
newline. Rejected rather than stripped — a stripped value is a guess, and the same
|
|
214
|
+
guess is wrong for a space in the middle of a secret.
|
|
215
|
+
- **Swapped fields.** A `client_secret` beginning `tmpr_` is a temper *client id* in
|
|
216
|
+
the secret's slot; temper mints secrets as bare base64url.
|
|
217
|
+
|
|
218
|
+
The mint itself is deliberately unadventurous. It does not follow redirects (that
|
|
219
|
+
would re-POST the `client_secret` to whatever origin the `Location` names), it reads
|
|
220
|
+
at most 64 KiB of response, it applies a connect/read timeout — the mint runs under a
|
|
221
|
+
lock, so an issuer that accepts the connection and never answers would otherwise
|
|
222
|
+
block every thread in the process, not one — and it treats a 200 that carries no
|
|
223
|
+
usable `access_token`, `token_type` or `expires_in` as a credential failure rather
|
|
224
|
+
than letting a `KeyError` out of `token()`. A mint that fails drops the cached token
|
|
225
|
+
first, so a caller never goes on presenting one the server has already rejected.
|
|
226
|
+
|
|
227
|
+
## Development
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
uv sync --group dev # honours .python-version
|
|
231
|
+
uv run ruff check . && uv run ruff format --check .
|
|
232
|
+
uv run mypy
|
|
233
|
+
uv run pytest
|
|
234
|
+
uv build # the gemspec-equivalent smoke test
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
There is no committed lockfile — a library states what it works with rather than
|
|
238
|
+
freezing what its consumers resolve, the same call `temper-rb` makes by gitignoring
|
|
239
|
+
`Gemfile.lock`. What a lockfile would have bought is bought instead by pinning `ruff`
|
|
240
|
+
and `mypy` EXACTLY in `pyproject.toml`: those two can redden CI on their own release
|
|
241
|
+
schedule, and nothing else in the dev group can.
|
|
242
|
+
|
|
243
|
+
Supported interpreters are **3.10+**. That is above the generated package's own `>= 3.9`
|
|
244
|
+
on purpose: 3.9 is end-of-life, and mypy 2.x already requires 3.10, so a 3.9 floor would
|
|
245
|
+
promise a version whose type-check we could not run.
|
|
246
|
+
|
|
247
|
+
Regenerating needs no Python at all — a Rust developer who changed a DTO can run
|
|
248
|
+
`cargo make openapi-py` with either Docker or a JVM. The generator pin and its
|
|
249
|
+
parameters live in one place, `.github/scripts/generate-temper-py.sh`, shared by
|
|
250
|
+
cargo-make and the drift gate.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# temper-py
|
|
2
|
+
|
|
3
|
+
The Python client for the [Temper](https://github.com/tasker-systems/temper)
|
|
4
|
+
knowledge-base API. Sibling of `temper-rb` and `temper-ts` — all three are pinned to
|
|
5
|
+
the same OpenAPI contract. Workspace-isolated: this package is not a cargo member and
|
|
6
|
+
not a bun workspace member, so nothing in the repo builds it implicitly.
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
import temper
|
|
10
|
+
from temper.generated.api.resources_api import ResourcesApi
|
|
11
|
+
|
|
12
|
+
temper.configure(base_url="https://temperkb.io")
|
|
13
|
+
|
|
14
|
+
client = temper.Client(
|
|
15
|
+
temper.ClientCredentials(
|
|
16
|
+
token_url="https://temperkb.io/oauth/token",
|
|
17
|
+
client_id="tmpr_...",
|
|
18
|
+
client_secret="...",
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
client.whoami()
|
|
23
|
+
client.call(lambda api: ResourcesApi(api).get_resource(resource_id), idempotent=True)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## What is generated and what is not
|
|
27
|
+
|
|
28
|
+
`temper/generated/**` is emitted from the repo-root `openapi.json` — itself a product
|
|
29
|
+
of the Axum router — by a pinned `openapi-generator`. **Never hand-edit it.** A new
|
|
30
|
+
field on a response DTO restales it exactly as it restales `openapi.json` itself, the
|
|
31
|
+
`temper-rb` gem, and `temper-ts`'s `schema.ts`:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cargo make openapi # regenerates the spec and all three SDKs
|
|
35
|
+
cargo make openapi-py # just this package
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`cargo make check` runs `openapi-py-drift`, which regenerates and diffs. The
|
|
39
|
+
`test-python` CI job runs the same check and never skips, so a contract change that
|
|
40
|
+
leaves this package behind cannot merge.
|
|
41
|
+
|
|
42
|
+
Everything outside `temper/generated/` is hand-written, and
|
|
43
|
+
`.openapi-generator-ignore` keeps the generator off it.
|
|
44
|
+
|
|
45
|
+
| Module | What it is |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `temper.connection` | One `ApiClient` (one urllib3 pool) per process; the token is call-scoped via a `ContextVar` |
|
|
48
|
+
| `temper.credentials` | `BearerToken` and `ClientCredentials`, pinned to `tests/contracts/m2m-token-request.json` |
|
|
49
|
+
| `temper.errors` | The transient/permanent split, and `map_error` |
|
|
50
|
+
| `temper.client` | `Client.call()` — the one seam carrying retry policy and 401 repair |
|
|
51
|
+
| `temper.act` | `ActInput`'s seven wire keys, with the confidence invariant enforced at construction |
|
|
52
|
+
| `temper.refs` | `parse_ref` — a port of `temper_workflow::operations::parse_ref` |
|
|
53
|
+
| `temper._validate` | The admission checks the seams share: endpoints, and values that become headers |
|
|
54
|
+
|
|
55
|
+
### There are no per-endpoint wrapper methods, deliberately
|
|
56
|
+
|
|
57
|
+
The gem hand-writes `Resources`, `Contexts`, `CognitiveMaps` because a Ruby caller
|
|
58
|
+
otherwise passes an untyped hash. The generated Python core already answers that:
|
|
59
|
+
every operation is a typed method over pydantic models. A hand-written
|
|
60
|
+
`resources.create(...)` would be a second, worse spelling of something already
|
|
61
|
+
correct — and a place for the two to drift. `temper-ts` declines the same wrappers
|
|
62
|
+
for the same reason.
|
|
63
|
+
|
|
64
|
+
What the generated core does *not* answer is which failures are worth retrying and
|
|
65
|
+
who repairs a dead token. That is what `Client.call()` is:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
client.call(fn, idempotent=True) # a safe method: 5xx and transport failures retry
|
|
69
|
+
client.call(fn) # a write: NEVER auto-retried
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A 401 is repaired once either way — re-authenticating is not re-submitting. A
|
|
73
|
+
`BearerToken` cannot mint, so its 401 comes back untouched rather than being replaced
|
|
74
|
+
by a message about the client's own plumbing.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### What `configure()` will pass through
|
|
78
|
+
|
|
79
|
+
Keyword arguments beyond `base_url` and `device_id` reach the generated
|
|
80
|
+
`Configuration`, and only the ones on an **allowlist** do:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
temper.configure(
|
|
84
|
+
base_url="https://temper.internal",
|
|
85
|
+
ssl_ca_cert="/etc/ssl/private-ca.pem", # trust a private CA
|
|
86
|
+
tls_server_name="temper.internal", # the SNI name the cert carries
|
|
87
|
+
connection_pool_maxsize=32,
|
|
88
|
+
proxy="http://proxy.internal:3128",
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`ssl_ca_cert`, `ca_cert_data`, `cert_file`, `key_file`, `tls_server_name`,
|
|
93
|
+
`connection_pool_maxsize`, `proxy`, `proxy_headers`, `socket_options`,
|
|
94
|
+
`datetime_format`, `date_format` — that is the whole list, and an unrecognised name
|
|
95
|
+
is a `TypeError` rather than a silent passthrough. Three of the arguments it refuses
|
|
96
|
+
are why the list is an allowlist:
|
|
97
|
+
|
|
98
|
+
| Refused | What it would have done |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `debug=True` | Sets `httplib.HTTPConnection.debuglevel = 1`, a **class** attribute — every HTTP request in the process starts printing its request headers to stdout, `Authorization: Bearer …` included. Raise the level on the `temper.generated` or `urllib3` logger instead; neither touches httplib. |
|
|
101
|
+
| `verify_ssl=False` | `ssl.CERT_NONE` on the pool: any certificate from anything that answers, and the bearer token goes to whoever intercepted the connection. Use `ssl_ca_cert` / `ca_cert_data`. |
|
|
102
|
+
| `assert_hostname=False` | Keeps verification on but stops checking the certificate is for the host you dialled. Use `tls_server_name`. |
|
|
103
|
+
|
|
104
|
+
The rest are refused because this module owns them (`host`, `retries`, the
|
|
105
|
+
`server_*` family) or because credentials are call-scoped, not connection-scoped
|
|
106
|
+
(`access_token`, `api_key`, `username`/`password`). Every refusal names its reason,
|
|
107
|
+
and all of them fire at `configure()` — not at the first API call, which is when a
|
|
108
|
+
lazily-built `Configuration` would have raised.
|
|
109
|
+
|
|
110
|
+
### Endpoints, and plaintext http
|
|
111
|
+
|
|
112
|
+
`base_url` and `ClientCredentials(token_url=...)` must be absolute `https` URLs, with
|
|
113
|
+
no userinfo (`https://id:secret@host` puts the secret in every error message that
|
|
114
|
+
names the URL) and no query or fragment. Plaintext `http` is accepted for the
|
|
115
|
+
loopback interface — a test server, a `temper serve` on your laptop — and refused
|
|
116
|
+
anywhere else, because a bearer token and a `client_secret` both travel in the clear
|
|
117
|
+
over it. Where TLS genuinely terminates elsewhere, say so:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
temper.configure(base_url="http://temper.internal", allow_insecure_http=True)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Connections and forking
|
|
124
|
+
|
|
125
|
+
`temper.configure()` installs a process-global connection: one `ApiClient`, one
|
|
126
|
+
urllib3 pool, one TLS handshake amortized across every call. The access token is *not*
|
|
127
|
+
on it — it is bound per call from the `Client`'s credential, through a `ContextVar`,
|
|
128
|
+
so one connection serves every concurrent caller with their own identity.
|
|
129
|
+
|
|
130
|
+
urllib3 has no fork hook (the gem gets one free from `connection_pool >= 2.4`), so a
|
|
131
|
+
forking server must drop the inherited sockets itself:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import os, temper
|
|
135
|
+
|
|
136
|
+
os.register_at_fork(after_in_child=temper.reset_connection)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Errors
|
|
140
|
+
|
|
141
|
+
`map_error` translates the generated core's `ApiException` — and the raw `urllib3`
|
|
142
|
+
errors a transport failure raises — into a tree whose top-level split is the one that
|
|
143
|
+
matters operationally:
|
|
144
|
+
|
|
145
|
+
- `TransientError` → `ServerError`, `RateLimited` (with `retry_after`), `TransportError`.
|
|
146
|
+
Let these escape a job; a retry is what fixes them.
|
|
147
|
+
- `PermanentError` → `Unauthorized`, `Forbidden`, `SystemAccessRequired`, `NotFound`,
|
|
148
|
+
`Conflict`, `BadRequest`. Catch these and dead-letter them.
|
|
149
|
+
|
|
150
|
+
`SystemAccessRequired.refusal` returns the typed refusal the server sent (`Denied`,
|
|
151
|
+
`Revoked`, `IllegalTransition`, …) so a worker can tell "never granted" from "granted
|
|
152
|
+
and then revoked" without matching on a message string. `refusal_kind` gives the raw
|
|
153
|
+
discriminator when this build predates the kind the server named.
|
|
154
|
+
|
|
155
|
+
> The gem calls the transport failure `Temper::ConnectionError`. Here it is
|
|
156
|
+
> `TransportError`, because `ConnectionError` is a Python builtin and shadowing it
|
|
157
|
+
> would make `except ConnectionError` silently catch the wrong thing.
|
|
158
|
+
|
|
159
|
+
### Composition bounds raise BEFORE the request `[2026-08-28]`
|
|
160
|
+
|
|
161
|
+
`/api/query`'s contract publishes ceilings on what one composition may declare, and the
|
|
162
|
+
generated pydantic models enforce them locally — so these surface as a `ValidationError`
|
|
163
|
+
at construction (and, since `validate_assignment` is on, at mutation) rather than as a
|
|
164
|
+
`BadRequest` carrying a typed refusal:
|
|
165
|
+
|
|
166
|
+
| field | ceiling |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `Composition.stages` | 64 |
|
|
169
|
+
| `Intention.query` | 4096 |
|
|
170
|
+
| `IdSet.ids` | 256 |
|
|
171
|
+
| `ResourceFilter.doc_type` / `.tags`, `EdgeFilter.labels` | 256 |
|
|
172
|
+
|
|
173
|
+
**This is a behaviour change for code that already builds large plans**: a 300-id `IdSet`
|
|
174
|
+
used to construct fine and reach the server. It now raises before any HTTP call.
|
|
175
|
+
|
|
176
|
+
**The client counts characters; the server counts bytes.** A 4096-character CJK question is
|
|
177
|
+
8192 bytes — it constructs cleanly here and is refused server-side as `intention_too_long`.
|
|
178
|
+
The skew is one-directional by construction (a UTF-8 string is never fewer bytes than
|
|
179
|
+
characters), so the client can only ever under-enforce, never refuse something the server
|
|
180
|
+
would have run.
|
|
181
|
+
|
|
182
|
+
Two ceilings are deliberately NOT enforced here, because neither is a contract fact: the
|
|
183
|
+
per-stage predicate and probe caps, and the aggregate embed budget
|
|
184
|
+
(`intention_budget_exceeded`) — what a deployment can embed in one request is a property of
|
|
185
|
+
that deployment. Those arrive as refusals.
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
## Credentials
|
|
189
|
+
|
|
190
|
+
`BearerToken(token)` and `ClientCredentials(...)` check their inputs at construction,
|
|
191
|
+
because the two ways a credential arrives wrong both surface as an unexplained
|
|
192
|
+
`invalid_client` or 401 hours later:
|
|
193
|
+
|
|
194
|
+
- **Whitespace.** `TEMPER_M2M_CLIENT_SECRET=$(cat secret.txt)` keeps the trailing
|
|
195
|
+
newline. Rejected rather than stripped — a stripped value is a guess, and the same
|
|
196
|
+
guess is wrong for a space in the middle of a secret.
|
|
197
|
+
- **Swapped fields.** A `client_secret` beginning `tmpr_` is a temper *client id* in
|
|
198
|
+
the secret's slot; temper mints secrets as bare base64url.
|
|
199
|
+
|
|
200
|
+
The mint itself is deliberately unadventurous. It does not follow redirects (that
|
|
201
|
+
would re-POST the `client_secret` to whatever origin the `Location` names), it reads
|
|
202
|
+
at most 64 KiB of response, it applies a connect/read timeout — the mint runs under a
|
|
203
|
+
lock, so an issuer that accepts the connection and never answers would otherwise
|
|
204
|
+
block every thread in the process, not one — and it treats a 200 that carries no
|
|
205
|
+
usable `access_token`, `token_type` or `expires_in` as a credential failure rather
|
|
206
|
+
than letting a `KeyError` out of `token()`. A mint that fails drops the cached token
|
|
207
|
+
first, so a caller never goes on presenting one the server has already rejected.
|
|
208
|
+
|
|
209
|
+
## Development
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
uv sync --group dev # honours .python-version
|
|
213
|
+
uv run ruff check . && uv run ruff format --check .
|
|
214
|
+
uv run mypy
|
|
215
|
+
uv run pytest
|
|
216
|
+
uv build # the gemspec-equivalent smoke test
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
There is no committed lockfile — a library states what it works with rather than
|
|
220
|
+
freezing what its consumers resolve, the same call `temper-rb` makes by gitignoring
|
|
221
|
+
`Gemfile.lock`. What a lockfile would have bought is bought instead by pinning `ruff`
|
|
222
|
+
and `mypy` EXACTLY in `pyproject.toml`: those two can redden CI on their own release
|
|
223
|
+
schedule, and nothing else in the dev group can.
|
|
224
|
+
|
|
225
|
+
Supported interpreters are **3.10+**. That is above the generated package's own `>= 3.9`
|
|
226
|
+
on purpose: 3.9 is end-of-life, and mypy 2.x already requires 3.10, so a 3.9 floor would
|
|
227
|
+
promise a version whose type-check we could not run.
|
|
228
|
+
|
|
229
|
+
Regenerating needs no Python at all — a Rust developer who changed a DTO can run
|
|
230
|
+
`cargo make openapi-py` with either Docker or a JVM. The generator pin and its
|
|
231
|
+
parameters live in one place, `.github/scripts/generate-temper-py.sh`, shared by
|
|
232
|
+
cargo-make and the drift gate.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Hand-written, and listed in .openapi-generator-ignore so it stays that way. The
|
|
2
|
+
# generator emits a pyproject of its own (Poetry-flavoured, naming the package
|
|
3
|
+
# `temper.generated`); ours describes the SDK, whose distribution is `temperkb-py`
|
|
4
|
+
# and whose import package is `temper`. PyPI has no scopes, and both natural
|
|
5
|
+
# distribution names were already taken by unrelated projects (temper-py — a TEMPer
|
|
6
|
+
# USB-device reader; temper — an HTML DSL), so the distribution carries the kb.
|
|
7
|
+
# Nothing under temper/ changes with the name: the wheel still installs `temper`.
|
|
8
|
+
[project]
|
|
9
|
+
name = "temperkb-py"
|
|
10
|
+
dynamic = ["version"]
|
|
11
|
+
description = "Python SDK for the Temper knowledge-base API"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { text = "MIT" }
|
|
14
|
+
authors = [{ name = "Pete Taylor", email = "pete.jc.taylor@hey.com" }]
|
|
15
|
+
keywords = ["temper", "knowledge-base", "openapi", "sdk"]
|
|
16
|
+
|
|
17
|
+
# ABOVE the generated package's own >= 3.9, deliberately. 3.9 reached end of life in
|
|
18
|
+
# October 2025, so promising it would be promising a version that receives no security
|
|
19
|
+
# fixes — and the dev toolchain has already moved: mypy 2.x requires >= 3.10, so a 3.9
|
|
20
|
+
# floor makes the type-check ungettable rather than merely generous. The dev pin in
|
|
21
|
+
# .python-version is newer again, exactly as the gem's >= 3.1 floor sits under a 3.4.10
|
|
22
|
+
# pin: a floor is a promise to callers, a pin is a statement about this checkout.
|
|
23
|
+
requires-python = ">=3.10"
|
|
24
|
+
|
|
25
|
+
# Every one of these is imported by the GENERATED tree, not by the skin — the
|
|
26
|
+
# floors are the generator's own (see the pyproject it emits and we ignore).
|
|
27
|
+
# urllib3 is the `--library=urllib3` transport; the skin reuses it to mint M2M
|
|
28
|
+
# tokens rather than pulling in a second HTTP client for one POST.
|
|
29
|
+
dependencies = [
|
|
30
|
+
"urllib3>=2.1,<3.0",
|
|
31
|
+
"python-dateutil>=2.8.2",
|
|
32
|
+
"pydantic>=2.11",
|
|
33
|
+
"typing-extensions>=4.7.1",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://github.com/tasker-systems/temper"
|
|
38
|
+
Source = "https://github.com/tasker-systems/temper/tree/main/clients/temper-py"
|
|
39
|
+
Issues = "https://github.com/tasker-systems/temper/issues"
|
|
40
|
+
|
|
41
|
+
# ruff and mypy are pinned EXACTLY — no caret, no floor — for the reason temper-ts
|
|
42
|
+
# pins openapi-typescript exactly: a linter that moves on its own redden CI on a day
|
|
43
|
+
# when nothing in this repo changed, and the fix is then somebody else's release notes
|
|
44
|
+
# rather than the diff under review. Bump them deliberately, in their own commit.
|
|
45
|
+
# pytest and the stubs get floors: neither can fail a build by gaining an opinion.
|
|
46
|
+
#
|
|
47
|
+
# There is no committed lockfile, matching the gem (whose Gemfile.lock is gitignored)
|
|
48
|
+
# and the library convention it follows: a library's job is to state what it works
|
|
49
|
+
# with, not to freeze what its consumers resolve. The two tools that CAN break a build
|
|
50
|
+
# on their own schedule are pinned above instead, which is the part a lockfile would
|
|
51
|
+
# have been buying.
|
|
52
|
+
[dependency-groups]
|
|
53
|
+
dev = [
|
|
54
|
+
"pytest>=8.0",
|
|
55
|
+
"mypy==2.3.1",
|
|
56
|
+
"ruff==0.16.5",
|
|
57
|
+
"types-python-dateutil>=2.8.19.14",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[build-system]
|
|
61
|
+
requires = ["hatchling"]
|
|
62
|
+
build-backend = "hatchling.build"
|
|
63
|
+
|
|
64
|
+
# Read, not imported. `temper/version.py` must stand alone for the same reason the
|
|
65
|
+
# gem's `lib/temper/version.rb` must: a build that imported `temper` to learn its
|
|
66
|
+
# version would drag in pydantic and urllib3 at build time, and a regression there
|
|
67
|
+
# is invisible to pytest and fatal to `python -m build`. Hatchling regexes the file.
|
|
68
|
+
[tool.hatch.version]
|
|
69
|
+
path = "temper/version.py"
|
|
70
|
+
|
|
71
|
+
[tool.hatch.build.targets.wheel]
|
|
72
|
+
packages = ["temper"]
|
|
73
|
+
|
|
74
|
+
[tool.hatch.build.targets.sdist]
|
|
75
|
+
include = ["temper", "README.md", "LICENSE", "pyproject.toml"]
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# ruff
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
[tool.ruff]
|
|
81
|
+
target-version = "py310"
|
|
82
|
+
line-length = 100
|
|
83
|
+
# `generate-temper-py.sh` is the only writer of the generated tree. Linting it
|
|
84
|
+
# would mean hand-editing it — the same exclusion, for the same reason, that
|
|
85
|
+
# .rubocop.yml carves out for lib/temper/generated/**.
|
|
86
|
+
extend-exclude = ["temper/generated"]
|
|
87
|
+
|
|
88
|
+
[tool.ruff.lint]
|
|
89
|
+
select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"]
|
|
90
|
+
|
|
91
|
+
[tool.ruff.lint.per-file-ignores]
|
|
92
|
+
# Asserting on a private module attribute is the point of some of these tests.
|
|
93
|
+
"tests/*" = ["SLF001"]
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# mypy
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
[tool.mypy]
|
|
99
|
+
# The package's own floor, so the checker sees what the oldest supported interpreter
|
|
100
|
+
# sees.
|
|
101
|
+
python_version = "3.10"
|
|
102
|
+
files = ["temper", "tests"]
|
|
103
|
+
strict = true
|
|
104
|
+
# The generated tree is excluded for the ruff reason above, and re-admitted as an
|
|
105
|
+
# untyped-but-followed import so the skin's own annotations still resolve against
|
|
106
|
+
# real generated classes rather than against Any.
|
|
107
|
+
exclude = ["^temper/generated/"]
|
|
108
|
+
|
|
109
|
+
# `silent`, not `skip`. Skipping makes every generated symbol `Any`, which quietly
|
|
110
|
+
# turns the skin's own annotations into no-ops — `TokenScopedConfiguration` would be
|
|
111
|
+
# subclassing `Any`, and the property it overrides would be checked against nothing.
|
|
112
|
+
# Silent still analyses the tree (so the skin is checked against the REAL generated
|
|
113
|
+
# classes) while suppressing errors inside code no human may edit.
|
|
114
|
+
[[tool.mypy.overrides]]
|
|
115
|
+
module = "temper.generated.*"
|
|
116
|
+
follow_imports = "silent"
|
|
117
|
+
ignore_missing_imports = true
|
|
118
|
+
|
|
119
|
+
# Strict typing is a promise this package makes to CALLERS; a test body annotating
|
|
120
|
+
# every `def test_x() -> None` buys nothing and hides the errors that matter under
|
|
121
|
+
# a hundred that do not. The strict checks that still apply here are the ones about
|
|
122
|
+
# using the package wrongly.
|
|
123
|
+
[[tool.mypy.overrides]]
|
|
124
|
+
module = "tests.*"
|
|
125
|
+
disallow_untyped_defs = false
|
|
126
|
+
disallow_incomplete_defs = false
|
|
127
|
+
disallow_untyped_calls = false
|
|
128
|
+
|
|
129
|
+
[tool.pytest.ini_options]
|
|
130
|
+
testpaths = ["tests"]
|