wandb 0.21.2__py3-none-macosx_12_0_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package_readme.md +97 -0
- wandb/__init__.py +248 -0
- wandb/__init__.pyi +1230 -0
- wandb/__main__.py +3 -0
- wandb/_iterutils.py +65 -0
- wandb/_pydantic/__init__.py +30 -0
- wandb/_pydantic/base.py +128 -0
- wandb/_pydantic/utils.py +80 -0
- wandb/_pydantic/v1_compat.py +284 -0
- wandb/agents/__init__.py +0 -0
- wandb/agents/pyagent.py +386 -0
- wandb/analytics/__init__.py +3 -0
- wandb/analytics/sentry.py +267 -0
- wandb/apis/__init__.py +48 -0
- wandb/apis/attrs.py +50 -0
- wandb/apis/importers/__init__.py +1 -0
- wandb/apis/importers/internals/internal.py +382 -0
- wandb/apis/importers/internals/protocols.py +103 -0
- wandb/apis/importers/internals/util.py +78 -0
- wandb/apis/importers/mlflow.py +254 -0
- wandb/apis/importers/validation.py +108 -0
- wandb/apis/importers/wandb.py +1608 -0
- wandb/apis/internal.py +239 -0
- wandb/apis/normalize.py +81 -0
- wandb/apis/paginator.py +138 -0
- wandb/apis/public/__init__.py +35 -0
- wandb/apis/public/api.py +2449 -0
- wandb/apis/public/artifacts.py +1046 -0
- wandb/apis/public/automations.py +85 -0
- wandb/apis/public/const.py +4 -0
- wandb/apis/public/files.py +402 -0
- wandb/apis/public/history.py +201 -0
- wandb/apis/public/integrations.py +203 -0
- wandb/apis/public/jobs.py +742 -0
- wandb/apis/public/projects.py +276 -0
- wandb/apis/public/query_generator.py +176 -0
- wandb/apis/public/registries/__init__.py +0 -0
- wandb/apis/public/registries/_freezable_list.py +179 -0
- wandb/apis/public/registries/_utils.py +138 -0
- wandb/apis/public/registries/registries_search.py +347 -0
- wandb/apis/public/registries/registry.py +358 -0
- wandb/apis/public/reports.py +595 -0
- wandb/apis/public/runs.py +1216 -0
- wandb/apis/public/sweeps.py +440 -0
- wandb/apis/public/teams.py +235 -0
- wandb/apis/public/users.py +177 -0
- wandb/apis/public/utils.py +210 -0
- wandb/apis/reports/__init__.py +1 -0
- wandb/apis/reports/v1/__init__.py +8 -0
- wandb/apis/reports/v2/__init__.py +8 -0
- wandb/apis/workspaces/__init__.py +8 -0
- wandb/automations/__init__.py +73 -0
- wandb/automations/_filters/__init__.py +40 -0
- wandb/automations/_filters/expressions.py +181 -0
- wandb/automations/_filters/operators.py +258 -0
- wandb/automations/_filters/run_metrics.py +330 -0
- wandb/automations/_generated/__init__.py +177 -0
- wandb/automations/_generated/create_automation.py +17 -0
- wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
- wandb/automations/_generated/delete_automation.py +15 -0
- wandb/automations/_generated/enums.py +35 -0
- wandb/automations/_generated/fragments.py +358 -0
- wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
- wandb/automations/_generated/get_automations.py +24 -0
- wandb/automations/_generated/get_automations_by_entity.py +26 -0
- wandb/automations/_generated/input_types.py +104 -0
- wandb/automations/_generated/integrations_by_entity.py +22 -0
- wandb/automations/_generated/operations.py +647 -0
- wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
- wandb/automations/_generated/update_automation.py +17 -0
- wandb/automations/_utils.py +235 -0
- wandb/automations/_validators.py +165 -0
- wandb/automations/actions.py +218 -0
- wandb/automations/automations.py +85 -0
- wandb/automations/events.py +285 -0
- wandb/automations/integrations.py +45 -0
- wandb/automations/scopes.py +78 -0
- wandb/beta/workflows.py +324 -0
- wandb/bin/gpu_stats +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/__init__.py +0 -0
- wandb/cli/beta.py +175 -0
- wandb/cli/cli.py +2883 -0
- wandb/data_types.py +66 -0
- wandb/docker/__init__.py +290 -0
- wandb/docker/names.py +40 -0
- wandb/docker/wandb-entrypoint.sh +33 -0
- wandb/env.py +535 -0
- wandb/errors/__init__.py +17 -0
- wandb/errors/errors.py +40 -0
- wandb/errors/links.py +73 -0
- wandb/errors/term.py +415 -0
- wandb/errors/util.py +57 -0
- wandb/errors/warnings.py +2 -0
- wandb/filesync/__init__.py +0 -0
- wandb/filesync/dir_watcher.py +404 -0
- wandb/filesync/stats.py +100 -0
- wandb/filesync/step_checksum.py +142 -0
- wandb/filesync/step_prepare.py +179 -0
- wandb/filesync/step_upload.py +287 -0
- wandb/filesync/upload_job.py +142 -0
- wandb/integration/__init__.py +0 -0
- wandb/integration/catboost/__init__.py +5 -0
- wandb/integration/catboost/catboost.py +182 -0
- wandb/integration/cohere/__init__.py +3 -0
- wandb/integration/cohere/cohere.py +21 -0
- wandb/integration/cohere/resolver.py +347 -0
- wandb/integration/diffusers/__init__.py +3 -0
- wandb/integration/diffusers/autologger.py +76 -0
- wandb/integration/diffusers/pipeline_resolver.py +50 -0
- wandb/integration/diffusers/resolvers/__init__.py +9 -0
- wandb/integration/diffusers/resolvers/multimodal.py +881 -0
- wandb/integration/diffusers/resolvers/utils.py +102 -0
- wandb/integration/fastai/__init__.py +243 -0
- wandb/integration/gym/__init__.py +98 -0
- wandb/integration/huggingface/__init__.py +3 -0
- wandb/integration/huggingface/huggingface.py +18 -0
- wandb/integration/huggingface/resolver.py +213 -0
- wandb/integration/keras/__init__.py +11 -0
- wandb/integration/keras/callbacks/__init__.py +5 -0
- wandb/integration/keras/callbacks/metrics_logger.py +129 -0
- wandb/integration/keras/callbacks/model_checkpoint.py +188 -0
- wandb/integration/keras/callbacks/tables_builder.py +228 -0
- wandb/integration/keras/keras.py +1086 -0
- wandb/integration/kfp/__init__.py +6 -0
- wandb/integration/kfp/helpers.py +28 -0
- wandb/integration/kfp/kfp_patch.py +335 -0
- wandb/integration/kfp/wandb_logging.py +182 -0
- wandb/integration/langchain/__init__.py +3 -0
- wandb/integration/langchain/wandb_tracer.py +49 -0
- wandb/integration/lightgbm/__init__.py +239 -0
- wandb/integration/lightning/__init__.py +0 -0
- wandb/integration/lightning/fabric/__init__.py +3 -0
- wandb/integration/lightning/fabric/logger.py +763 -0
- wandb/integration/metaflow/__init__.py +9 -0
- wandb/integration/metaflow/data_pandas.py +74 -0
- wandb/integration/metaflow/data_pytorch.py +75 -0
- wandb/integration/metaflow/data_sklearn.py +76 -0
- wandb/integration/metaflow/errors.py +13 -0
- wandb/integration/metaflow/metaflow.py +327 -0
- wandb/integration/openai/__init__.py +3 -0
- wandb/integration/openai/fine_tuning.py +480 -0
- wandb/integration/openai/openai.py +22 -0
- wandb/integration/openai/resolver.py +240 -0
- wandb/integration/prodigy/__init__.py +3 -0
- wandb/integration/prodigy/prodigy.py +291 -0
- wandb/integration/sacred/__init__.py +117 -0
- wandb/integration/sagemaker/__init__.py +14 -0
- wandb/integration/sagemaker/auth.py +29 -0
- wandb/integration/sagemaker/config.py +58 -0
- wandb/integration/sagemaker/files.py +2 -0
- wandb/integration/sagemaker/resources.py +63 -0
- wandb/integration/sb3/__init__.py +3 -0
- wandb/integration/sb3/sb3.py +147 -0
- wandb/integration/sklearn/__init__.py +37 -0
- wandb/integration/sklearn/calculate/__init__.py +32 -0
- wandb/integration/sklearn/calculate/calibration_curves.py +125 -0
- wandb/integration/sklearn/calculate/class_proportions.py +68 -0
- wandb/integration/sklearn/calculate/confusion_matrix.py +93 -0
- wandb/integration/sklearn/calculate/decision_boundaries.py +40 -0
- wandb/integration/sklearn/calculate/elbow_curve.py +55 -0
- wandb/integration/sklearn/calculate/feature_importances.py +67 -0
- wandb/integration/sklearn/calculate/learning_curve.py +64 -0
- wandb/integration/sklearn/calculate/outlier_candidates.py +69 -0
- wandb/integration/sklearn/calculate/residuals.py +86 -0
- wandb/integration/sklearn/calculate/silhouette.py +118 -0
- wandb/integration/sklearn/calculate/summary_metrics.py +62 -0
- wandb/integration/sklearn/plot/__init__.py +35 -0
- wandb/integration/sklearn/plot/classifier.py +329 -0
- wandb/integration/sklearn/plot/clusterer.py +146 -0
- wandb/integration/sklearn/plot/regressor.py +121 -0
- wandb/integration/sklearn/plot/shared.py +91 -0
- wandb/integration/sklearn/utils.py +184 -0
- wandb/integration/tensorboard/__init__.py +10 -0
- wandb/integration/tensorboard/log.py +351 -0
- wandb/integration/tensorboard/monkeypatch.py +186 -0
- wandb/integration/tensorflow/__init__.py +5 -0
- wandb/integration/tensorflow/estimator_hook.py +54 -0
- wandb/integration/torch/__init__.py +0 -0
- wandb/integration/torch/wandb_torch.py +554 -0
- wandb/integration/ultralytics/__init__.py +11 -0
- wandb/integration/ultralytics/bbox_utils.py +215 -0
- wandb/integration/ultralytics/callback.py +528 -0
- wandb/integration/ultralytics/classification_utils.py +83 -0
- wandb/integration/ultralytics/mask_utils.py +202 -0
- wandb/integration/ultralytics/pose_utils.py +103 -0
- wandb/integration/weave/__init__.py +6 -0
- wandb/integration/weave/interface.py +49 -0
- wandb/integration/weave/weave.py +63 -0
- wandb/integration/xgboost/__init__.py +11 -0
- wandb/integration/xgboost/xgboost.py +189 -0
- wandb/integration/yolov8/__init__.py +0 -0
- wandb/integration/yolov8/yolov8.py +284 -0
- wandb/jupyter.py +538 -0
- wandb/mpmain/__init__.py +0 -0
- wandb/mpmain/__main__.py +1 -0
- wandb/old/__init__.py +0 -0
- wandb/old/core.py +53 -0
- wandb/old/settings.py +176 -0
- wandb/old/summary.py +438 -0
- wandb/plot/__init__.py +30 -0
- wandb/plot/bar.py +71 -0
- wandb/plot/confusion_matrix.py +185 -0
- wandb/plot/custom_chart.py +147 -0
- wandb/plot/histogram.py +66 -0
- wandb/plot/line.py +75 -0
- wandb/plot/line_series.py +173 -0
- wandb/plot/pr_curve.py +186 -0
- wandb/plot/roc_curve.py +163 -0
- wandb/plot/scatter.py +66 -0
- wandb/plot/utils.py +184 -0
- wandb/plot/viz.py +41 -0
- wandb/proto/__init__.py +0 -0
- wandb/proto/v3/__init__.py +0 -0
- wandb/proto/v3/wandb_base_pb2.py +55 -0
- wandb/proto/v3/wandb_internal_pb2.py +1728 -0
- wandb/proto/v3/wandb_server_pb2.py +228 -0
- wandb/proto/v3/wandb_settings_pb2.py +122 -0
- wandb/proto/v3/wandb_telemetry_pb2.py +106 -0
- wandb/proto/v4/__init__.py +0 -0
- wandb/proto/v4/wandb_base_pb2.py +30 -0
- wandb/proto/v4/wandb_internal_pb2.py +382 -0
- wandb/proto/v4/wandb_server_pb2.py +67 -0
- wandb/proto/v4/wandb_settings_pb2.py +47 -0
- wandb/proto/v4/wandb_telemetry_pb2.py +41 -0
- wandb/proto/v5/wandb_base_pb2.py +31 -0
- wandb/proto/v5/wandb_internal_pb2.py +383 -0
- wandb/proto/v5/wandb_server_pb2.py +68 -0
- wandb/proto/v5/wandb_settings_pb2.py +48 -0
- wandb/proto/v5/wandb_telemetry_pb2.py +42 -0
- wandb/proto/v6/wandb_base_pb2.py +41 -0
- wandb/proto/v6/wandb_internal_pb2.py +393 -0
- wandb/proto/v6/wandb_server_pb2.py +78 -0
- wandb/proto/v6/wandb_settings_pb2.py +58 -0
- wandb/proto/v6/wandb_telemetry_pb2.py +52 -0
- wandb/proto/wandb_base_pb2.py +12 -0
- wandb/proto/wandb_deprecated.py +59 -0
- wandb/proto/wandb_generate_deprecated.py +30 -0
- wandb/proto/wandb_generate_proto.py +49 -0
- wandb/proto/wandb_internal_pb2.py +18 -0
- wandb/proto/wandb_server_pb2.py +12 -0
- wandb/proto/wandb_settings_pb2.py +12 -0
- wandb/proto/wandb_telemetry_pb2.py +12 -0
- wandb/py.typed +0 -0
- wandb/sdk/__init__.py +37 -0
- wandb/sdk/artifacts/__init__.py +0 -0
- wandb/sdk/artifacts/_factories.py +17 -0
- wandb/sdk/artifacts/_generated/__init__.py +508 -0
- wandb/sdk/artifacts/_generated/add_aliases.py +21 -0
- wandb/sdk/artifacts/_generated/artifact_by_id.py +17 -0
- wandb/sdk/artifacts/_generated/artifact_by_name.py +22 -0
- wandb/sdk/artifacts/_generated/artifact_collection_membership_file_urls.py +43 -0
- wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +43 -0
- wandb/sdk/artifacts/_generated/artifact_created_by.py +47 -0
- wandb/sdk/artifacts/_generated/artifact_file_urls.py +22 -0
- wandb/sdk/artifacts/_generated/artifact_type.py +31 -0
- wandb/sdk/artifacts/_generated/artifact_used_by.py +43 -0
- wandb/sdk/artifacts/_generated/artifact_version_files.py +36 -0
- wandb/sdk/artifacts/_generated/artifact_via_membership_by_name.py +26 -0
- wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +36 -0
- wandb/sdk/artifacts/_generated/delete_aliases.py +21 -0
- wandb/sdk/artifacts/_generated/delete_artifact.py +28 -0
- wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +25 -0
- wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +35 -0
- wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +35 -0
- wandb/sdk/artifacts/_generated/enums.py +22 -0
- wandb/sdk/artifacts/_generated/fetch_artifact_manifest.py +38 -0
- wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +67 -0
- wandb/sdk/artifacts/_generated/fetch_registries.py +32 -0
- wandb/sdk/artifacts/_generated/fragments.py +459 -0
- wandb/sdk/artifacts/_generated/input_types.py +46 -0
- wandb/sdk/artifacts/_generated/link_artifact.py +27 -0
- wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
- wandb/sdk/artifacts/_generated/operations.py +1223 -0
- wandb/sdk/artifacts/_generated/project_artifact_collection.py +101 -0
- wandb/sdk/artifacts/_generated/project_artifact_collections.py +33 -0
- wandb/sdk/artifacts/_generated/project_artifact_type.py +24 -0
- wandb/sdk/artifacts/_generated/project_artifact_types.py +24 -0
- wandb/sdk/artifacts/_generated/project_artifacts.py +42 -0
- wandb/sdk/artifacts/_generated/registry_collections.py +34 -0
- wandb/sdk/artifacts/_generated/registry_versions.py +34 -0
- wandb/sdk/artifacts/_generated/run_input_artifacts.py +51 -0
- wandb/sdk/artifacts/_generated/run_output_artifacts.py +51 -0
- wandb/sdk/artifacts/_generated/unlink_artifact.py +25 -0
- wandb/sdk/artifacts/_generated/update_artifact.py +26 -0
- wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +35 -0
- wandb/sdk/artifacts/_generated/update_artifact_sequence.py +35 -0
- wandb/sdk/artifacts/_graphql_fragments.py +19 -0
- wandb/sdk/artifacts/_internal_artifact.py +54 -0
- wandb/sdk/artifacts/_validators.py +309 -0
- wandb/sdk/artifacts/artifact.py +2702 -0
- wandb/sdk/artifacts/artifact_download_logger.py +45 -0
- wandb/sdk/artifacts/artifact_file_cache.py +251 -0
- wandb/sdk/artifacts/artifact_instance_cache.py +17 -0
- wandb/sdk/artifacts/artifact_manifest.py +76 -0
- wandb/sdk/artifacts/artifact_manifest_entry.py +258 -0
- wandb/sdk/artifacts/artifact_manifests/__init__.py +0 -0
- wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +94 -0
- wandb/sdk/artifacts/artifact_saver.py +277 -0
- wandb/sdk/artifacts/artifact_state.py +13 -0
- wandb/sdk/artifacts/artifact_ttl.py +9 -0
- wandb/sdk/artifacts/exceptions.py +71 -0
- wandb/sdk/artifacts/staging.py +27 -0
- wandb/sdk/artifacts/storage_handler.py +62 -0
- wandb/sdk/artifacts/storage_handlers/__init__.py +0 -0
- wandb/sdk/artifacts/storage_handlers/azure_handler.py +214 -0
- wandb/sdk/artifacts/storage_handlers/gcs_handler.py +224 -0
- wandb/sdk/artifacts/storage_handlers/http_handler.py +114 -0
- wandb/sdk/artifacts/storage_handlers/local_file_handler.py +142 -0
- wandb/sdk/artifacts/storage_handlers/multi_handler.py +56 -0
- wandb/sdk/artifacts/storage_handlers/s3_handler.py +339 -0
- wandb/sdk/artifacts/storage_handlers/tracking_handler.py +68 -0
- wandb/sdk/artifacts/storage_handlers/wb_artifact_handler.py +131 -0
- wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +74 -0
- wandb/sdk/artifacts/storage_layout.py +8 -0
- wandb/sdk/artifacts/storage_policies/__init__.py +4 -0
- wandb/sdk/artifacts/storage_policies/register.py +1 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +580 -0
- wandb/sdk/artifacts/storage_policy.py +75 -0
- wandb/sdk/backend/__init__.py +0 -0
- wandb/sdk/backend/backend.py +57 -0
- wandb/sdk/data_types/__init__.py +0 -0
- wandb/sdk/data_types/_dtypes.py +914 -0
- wandb/sdk/data_types/_private.py +10 -0
- wandb/sdk/data_types/audio.py +208 -0
- wandb/sdk/data_types/base_types/__init__.py +0 -0
- wandb/sdk/data_types/base_types/json_metadata.py +55 -0
- wandb/sdk/data_types/base_types/media.py +339 -0
- wandb/sdk/data_types/base_types/wb_value.py +295 -0
- wandb/sdk/data_types/bokeh.py +87 -0
- wandb/sdk/data_types/graph.py +439 -0
- wandb/sdk/data_types/helper_types/__init__.py +0 -0
- wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +327 -0
- wandb/sdk/data_types/helper_types/classes.py +159 -0
- wandb/sdk/data_types/helper_types/image_mask.py +251 -0
- wandb/sdk/data_types/histogram.py +107 -0
- wandb/sdk/data_types/html.py +165 -0
- wandb/sdk/data_types/image.py +974 -0
- wandb/sdk/data_types/molecule.py +250 -0
- wandb/sdk/data_types/object_3d.py +495 -0
- wandb/sdk/data_types/plotly.py +95 -0
- wandb/sdk/data_types/saved_model.py +435 -0
- wandb/sdk/data_types/table.py +1468 -0
- wandb/sdk/data_types/table_decorators.py +108 -0
- wandb/sdk/data_types/trace_tree.py +440 -0
- wandb/sdk/data_types/utils.py +260 -0
- wandb/sdk/data_types/video.py +303 -0
- wandb/sdk/integration_utils/__init__.py +0 -0
- wandb/sdk/integration_utils/auto_logging.py +232 -0
- wandb/sdk/integration_utils/data_logging.py +475 -0
- wandb/sdk/interface/__init__.py +0 -0
- wandb/sdk/interface/constants.py +4 -0
- wandb/sdk/interface/interface.py +1056 -0
- wandb/sdk/interface/interface_queue.py +40 -0
- wandb/sdk/interface/interface_shared.py +471 -0
- wandb/sdk/interface/interface_sock.py +49 -0
- wandb/sdk/interface/summary_record.py +67 -0
- wandb/sdk/internal/__init__.py +0 -0
- wandb/sdk/internal/_generated/__init__.py +15 -0
- wandb/sdk/internal/_generated/enums.py +4 -0
- wandb/sdk/internal/_generated/input_types.py +4 -0
- wandb/sdk/internal/_generated/operations.py +15 -0
- wandb/sdk/internal/_generated/server_features_query.py +27 -0
- wandb/sdk/internal/context.py +89 -0
- wandb/sdk/internal/datastore.py +293 -0
- wandb/sdk/internal/file_pusher.py +177 -0
- wandb/sdk/internal/file_stream.py +686 -0
- wandb/sdk/internal/handler.py +854 -0
- wandb/sdk/internal/incremental_table_util.py +53 -0
- wandb/sdk/internal/internal_api.py +4723 -0
- wandb/sdk/internal/job_builder.py +639 -0
- wandb/sdk/internal/profiler.py +79 -0
- wandb/sdk/internal/progress.py +77 -0
- wandb/sdk/internal/run.py +27 -0
- wandb/sdk/internal/sample.py +70 -0
- wandb/sdk/internal/sender.py +1692 -0
- wandb/sdk/internal/sender_config.py +203 -0
- wandb/sdk/internal/settings_static.py +120 -0
- wandb/sdk/internal/tb_watcher.py +519 -0
- wandb/sdk/internal/thread_local_settings.py +18 -0
- wandb/sdk/launch/__init__.py +15 -0
- wandb/sdk/launch/_launch.py +331 -0
- wandb/sdk/launch/_launch_add.py +255 -0
- wandb/sdk/launch/_project_spec.py +565 -0
- wandb/sdk/launch/agent/__init__.py +5 -0
- wandb/sdk/launch/agent/agent.py +931 -0
- wandb/sdk/launch/agent/config.py +296 -0
- wandb/sdk/launch/agent/job_status_tracker.py +55 -0
- wandb/sdk/launch/agent/run_queue_item_file_saver.py +39 -0
- wandb/sdk/launch/builder/__init__.py +0 -0
- wandb/sdk/launch/builder/abstract.py +156 -0
- wandb/sdk/launch/builder/build.py +296 -0
- wandb/sdk/launch/builder/context_manager.py +235 -0
- wandb/sdk/launch/builder/docker_builder.py +177 -0
- wandb/sdk/launch/builder/kaniko_builder.py +595 -0
- wandb/sdk/launch/builder/noop.py +58 -0
- wandb/sdk/launch/builder/templates/_wandb_bootstrap.py +188 -0
- wandb/sdk/launch/builder/templates/dockerfile.py +92 -0
- wandb/sdk/launch/create_job.py +541 -0
- wandb/sdk/launch/environment/abstract.py +29 -0
- wandb/sdk/launch/environment/aws_environment.py +322 -0
- wandb/sdk/launch/environment/azure_environment.py +105 -0
- wandb/sdk/launch/environment/gcp_environment.py +334 -0
- wandb/sdk/launch/environment/local_environment.py +65 -0
- wandb/sdk/launch/errors.py +13 -0
- wandb/sdk/launch/git_reference.py +109 -0
- wandb/sdk/launch/inputs/files.py +148 -0
- wandb/sdk/launch/inputs/internal.py +314 -0
- wandb/sdk/launch/inputs/manage.py +113 -0
- wandb/sdk/launch/inputs/schema.py +40 -0
- wandb/sdk/launch/loader.py +249 -0
- wandb/sdk/launch/registry/abstract.py +48 -0
- wandb/sdk/launch/registry/anon.py +29 -0
- wandb/sdk/launch/registry/azure_container_registry.py +124 -0
- wandb/sdk/launch/registry/elastic_container_registry.py +192 -0
- wandb/sdk/launch/registry/google_artifact_registry.py +219 -0
- wandb/sdk/launch/registry/local_registry.py +65 -0
- wandb/sdk/launch/runner/__init__.py +0 -0
- wandb/sdk/launch/runner/abstract.py +185 -0
- wandb/sdk/launch/runner/kubernetes_monitor.py +473 -0
- wandb/sdk/launch/runner/kubernetes_runner.py +1285 -0
- wandb/sdk/launch/runner/local_container.py +301 -0
- wandb/sdk/launch/runner/local_process.py +78 -0
- wandb/sdk/launch/runner/sagemaker_runner.py +424 -0
- wandb/sdk/launch/runner/vertex_runner.py +225 -0
- wandb/sdk/launch/sweeps/__init__.py +37 -0
- wandb/sdk/launch/sweeps/scheduler.py +739 -0
- wandb/sdk/launch/sweeps/scheduler_sweep.py +90 -0
- wandb/sdk/launch/sweeps/utils.py +324 -0
- wandb/sdk/launch/utils.py +746 -0
- wandb/sdk/launch/wandb_reference.py +138 -0
- wandb/sdk/lib/__init__.py +5 -0
- wandb/sdk/lib/apikey.py +334 -0
- wandb/sdk/lib/asyncio_compat.py +213 -0
- wandb/sdk/lib/asyncio_manager.py +252 -0
- wandb/sdk/lib/capped_dict.py +26 -0
- wandb/sdk/lib/config_util.py +101 -0
- wandb/sdk/lib/console_capture.py +219 -0
- wandb/sdk/lib/credentials.py +141 -0
- wandb/sdk/lib/deprecate.py +27 -0
- wandb/sdk/lib/disabled.py +30 -0
- wandb/sdk/lib/exit_hooks.py +54 -0
- wandb/sdk/lib/file_stream_utils.py +118 -0
- wandb/sdk/lib/filenames.py +64 -0
- wandb/sdk/lib/filesystem.py +372 -0
- wandb/sdk/lib/fsm.py +165 -0
- wandb/sdk/lib/gitlib.py +240 -0
- wandb/sdk/lib/gql_request.py +65 -0
- wandb/sdk/lib/handler_util.py +21 -0
- wandb/sdk/lib/hashutil.py +106 -0
- wandb/sdk/lib/import_hooks.py +275 -0
- wandb/sdk/lib/interrupt.py +37 -0
- wandb/sdk/lib/ipython.py +126 -0
- wandb/sdk/lib/json_util.py +75 -0
- wandb/sdk/lib/lazyloader.py +63 -0
- wandb/sdk/lib/module.py +72 -0
- wandb/sdk/lib/paths.py +106 -0
- wandb/sdk/lib/preinit.py +42 -0
- wandb/sdk/lib/printer.py +571 -0
- wandb/sdk/lib/printer_asyncio.py +48 -0
- wandb/sdk/lib/progress.py +320 -0
- wandb/sdk/lib/proto_util.py +90 -0
- wandb/sdk/lib/redirect.py +876 -0
- wandb/sdk/lib/retry.py +395 -0
- wandb/sdk/lib/run_moment.py +82 -0
- wandb/sdk/lib/runid.py +12 -0
- wandb/sdk/lib/server.py +58 -0
- wandb/sdk/lib/service/ipc_support.py +13 -0
- wandb/sdk/lib/service/service_client.py +106 -0
- wandb/sdk/lib/service/service_connection.py +192 -0
- wandb/sdk/lib/service/service_port_file.py +105 -0
- wandb/sdk/lib/service/service_process.py +111 -0
- wandb/sdk/lib/service/service_token.py +181 -0
- wandb/sdk/lib/sparkline.py +44 -0
- wandb/sdk/lib/telemetry.py +100 -0
- wandb/sdk/lib/timed_input.py +133 -0
- wandb/sdk/lib/timer.py +19 -0
- wandb/sdk/lib/wb_logging.py +161 -0
- wandb/sdk/mailbox/__init__.py +23 -0
- wandb/sdk/mailbox/mailbox.py +143 -0
- wandb/sdk/mailbox/mailbox_handle.py +132 -0
- wandb/sdk/mailbox/response_handle.py +99 -0
- wandb/sdk/mailbox/wait_with_progress.py +100 -0
- wandb/sdk/projects/_generated/__init__.py +47 -0
- wandb/sdk/projects/_generated/delete_project.py +22 -0
- wandb/sdk/projects/_generated/enums.py +4 -0
- wandb/sdk/projects/_generated/fetch_registry.py +22 -0
- wandb/sdk/projects/_generated/fragments.py +41 -0
- wandb/sdk/projects/_generated/input_types.py +13 -0
- wandb/sdk/projects/_generated/operations.py +88 -0
- wandb/sdk/projects/_generated/rename_project.py +27 -0
- wandb/sdk/projects/_generated/upsert_registry_project.py +27 -0
- wandb/sdk/verify/__init__.py +0 -0
- wandb/sdk/verify/verify.py +555 -0
- wandb/sdk/wandb_alerts.py +12 -0
- wandb/sdk/wandb_config.py +323 -0
- wandb/sdk/wandb_helper.py +54 -0
- wandb/sdk/wandb_init.py +1581 -0
- wandb/sdk/wandb_login.py +332 -0
- wandb/sdk/wandb_metric.py +112 -0
- wandb/sdk/wandb_require.py +88 -0
- wandb/sdk/wandb_require_helpers.py +44 -0
- wandb/sdk/wandb_run.py +4088 -0
- wandb/sdk/wandb_settings.py +2105 -0
- wandb/sdk/wandb_setup.py +560 -0
- wandb/sdk/wandb_summary.py +150 -0
- wandb/sdk/wandb_sweep.py +120 -0
- wandb/sdk/wandb_sync.py +71 -0
- wandb/sdk/wandb_watch.py +146 -0
- wandb/sklearn.py +35 -0
- wandb/sync/__init__.py +3 -0
- wandb/sync/sync.py +452 -0
- wandb/trigger.py +29 -0
- wandb/util.py +2040 -0
- wandb/vendor/__init__.py +0 -0
- wandb/vendor/gql-0.2.0/setup.py +40 -0
- wandb/vendor/gql-0.2.0/tests/__init__.py +0 -0
- wandb/vendor/gql-0.2.0/tests/starwars/__init__.py +0 -0
- wandb/vendor/gql-0.2.0/tests/starwars/fixtures.py +96 -0
- wandb/vendor/gql-0.2.0/tests/starwars/schema.py +146 -0
- wandb/vendor/gql-0.2.0/tests/starwars/test_dsl.py +293 -0
- wandb/vendor/gql-0.2.0/tests/starwars/test_query.py +355 -0
- wandb/vendor/gql-0.2.0/tests/starwars/test_validation.py +171 -0
- wandb/vendor/gql-0.2.0/tests/test_client.py +31 -0
- wandb/vendor/gql-0.2.0/tests/test_transport.py +89 -0
- wandb/vendor/gql-0.2.0/wandb_gql/__init__.py +4 -0
- wandb/vendor/gql-0.2.0/wandb_gql/client.py +75 -0
- wandb/vendor/gql-0.2.0/wandb_gql/dsl.py +152 -0
- wandb/vendor/gql-0.2.0/wandb_gql/gql.py +10 -0
- wandb/vendor/gql-0.2.0/wandb_gql/transport/__init__.py +0 -0
- wandb/vendor/gql-0.2.0/wandb_gql/transport/http.py +6 -0
- wandb/vendor/gql-0.2.0/wandb_gql/transport/local_schema.py +15 -0
- wandb/vendor/gql-0.2.0/wandb_gql/transport/requests.py +46 -0
- wandb/vendor/gql-0.2.0/wandb_gql/utils.py +21 -0
- wandb/vendor/graphql-core-1.1/setup.py +86 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/__init__.py +287 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/error/__init__.py +6 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/error/base.py +42 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/error/format_error.py +11 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/error/located_error.py +29 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/error/syntax_error.py +36 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/__init__.py +26 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/base.py +311 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executor.py +398 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/__init__.py +0 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/asyncio.py +53 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/gevent.py +22 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/process.py +32 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/sync.py +7 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/thread.py +35 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/utils.py +6 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/__init__.py +0 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/executor.py +66 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/fragment.py +252 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/resolver.py +151 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/utils.py +7 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/middleware.py +57 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/execution/values.py +145 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/graphql.py +60 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/__init__.py +0 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/ast.py +1349 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/base.py +19 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/lexer.py +435 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/location.py +30 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/parser.py +779 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/printer.py +193 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/source.py +18 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/visitor.py +222 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/language/visitor_meta.py +82 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/__init__.py +0 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/cached_property.py +17 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/contain_subset.py +28 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/default_ordered_dict.py +40 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/ordereddict.py +8 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/pair_set.py +43 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/version.py +78 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/type/__init__.py +67 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/type/definition.py +619 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/type/directives.py +132 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/type/introspection.py +440 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/type/scalars.py +131 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/type/schema.py +100 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/type/typemap.py +145 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/__init__.py +0 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/assert_valid_name.py +9 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/ast_from_value.py +65 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/ast_to_code.py +49 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/ast_to_dict.py +24 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/base.py +75 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/build_ast_schema.py +291 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/build_client_schema.py +250 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/concat_ast.py +9 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/extend_schema.py +357 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/get_field_def.py +27 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/get_operation_ast.py +21 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/introspection_query.py +90 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/is_valid_literal_value.py +67 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/is_valid_value.py +66 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/quoted_or_list.py +21 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/schema_printer.py +168 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/suggestion_list.py +56 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/type_comparators.py +69 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/type_from_ast.py +21 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/type_info.py +149 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/utils/value_from_ast.py +69 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/__init__.py +4 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/__init__.py +79 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/arguments_of_correct_type.py +24 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/base.py +8 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/default_values_of_correct_type.py +44 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/fields_on_correct_type.py +113 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/fragments_on_composite_types.py +33 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/known_argument_names.py +70 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/known_directives.py +97 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/known_fragment_names.py +19 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/known_type_names.py +43 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/lone_anonymous_operation.py +23 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/no_fragment_cycles.py +59 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/no_undefined_variables.py +36 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/no_unused_fragments.py +38 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/no_unused_variables.py +37 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/overlapping_fields_can_be_merged.py +529 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/possible_fragment_spreads.py +44 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/provided_non_null_arguments.py +46 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/scalar_leafs.py +33 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_argument_names.py +32 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_fragment_names.py +28 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_input_field_names.py +33 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_operation_names.py +31 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_variable_names.py +27 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/variables_are_input_types.py +21 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/variables_in_allowed_position.py +53 -0
- wandb/vendor/graphql-core-1.1/wandb_graphql/validation/validation.py +158 -0
- wandb/vendor/promise-2.3.0/conftest.py +30 -0
- wandb/vendor/promise-2.3.0/setup.py +64 -0
- wandb/vendor/promise-2.3.0/tests/__init__.py +0 -0
- wandb/vendor/promise-2.3.0/tests/conftest.py +8 -0
- wandb/vendor/promise-2.3.0/tests/test_awaitable.py +32 -0
- wandb/vendor/promise-2.3.0/tests/test_awaitable_35.py +47 -0
- wandb/vendor/promise-2.3.0/tests/test_benchmark.py +116 -0
- wandb/vendor/promise-2.3.0/tests/test_complex_threads.py +23 -0
- wandb/vendor/promise-2.3.0/tests/test_dataloader.py +452 -0
- wandb/vendor/promise-2.3.0/tests/test_dataloader_awaitable_35.py +99 -0
- wandb/vendor/promise-2.3.0/tests/test_dataloader_extra.py +65 -0
- wandb/vendor/promise-2.3.0/tests/test_extra.py +670 -0
- wandb/vendor/promise-2.3.0/tests/test_issues.py +132 -0
- wandb/vendor/promise-2.3.0/tests/test_promise_list.py +70 -0
- wandb/vendor/promise-2.3.0/tests/test_spec.py +584 -0
- wandb/vendor/promise-2.3.0/tests/test_thread_safety.py +115 -0
- wandb/vendor/promise-2.3.0/tests/utils.py +3 -0
- wandb/vendor/promise-2.3.0/wandb_promise/__init__.py +38 -0
- wandb/vendor/promise-2.3.0/wandb_promise/async_.py +135 -0
- wandb/vendor/promise-2.3.0/wandb_promise/compat.py +32 -0
- wandb/vendor/promise-2.3.0/wandb_promise/dataloader.py +326 -0
- wandb/vendor/promise-2.3.0/wandb_promise/iterate_promise.py +12 -0
- wandb/vendor/promise-2.3.0/wandb_promise/promise.py +848 -0
- wandb/vendor/promise-2.3.0/wandb_promise/promise_list.py +151 -0
- wandb/vendor/promise-2.3.0/wandb_promise/pyutils/__init__.py +0 -0
- wandb/vendor/promise-2.3.0/wandb_promise/pyutils/version.py +83 -0
- wandb/vendor/promise-2.3.0/wandb_promise/schedulers/__init__.py +0 -0
- wandb/vendor/promise-2.3.0/wandb_promise/schedulers/asyncio.py +22 -0
- wandb/vendor/promise-2.3.0/wandb_promise/schedulers/gevent.py +21 -0
- wandb/vendor/promise-2.3.0/wandb_promise/schedulers/immediate.py +27 -0
- wandb/vendor/promise-2.3.0/wandb_promise/schedulers/thread.py +18 -0
- wandb/vendor/promise-2.3.0/wandb_promise/utils.py +56 -0
- wandb/vendor/pygments/__init__.py +90 -0
- wandb/vendor/pygments/cmdline.py +568 -0
- wandb/vendor/pygments/console.py +74 -0
- wandb/vendor/pygments/filter.py +74 -0
- wandb/vendor/pygments/filters/__init__.py +350 -0
- wandb/vendor/pygments/formatter.py +95 -0
- wandb/vendor/pygments/formatters/__init__.py +153 -0
- wandb/vendor/pygments/formatters/_mapping.py +85 -0
- wandb/vendor/pygments/formatters/bbcode.py +109 -0
- wandb/vendor/pygments/formatters/html.py +851 -0
- wandb/vendor/pygments/formatters/img.py +600 -0
- wandb/vendor/pygments/formatters/irc.py +182 -0
- wandb/vendor/pygments/formatters/latex.py +482 -0
- wandb/vendor/pygments/formatters/other.py +160 -0
- wandb/vendor/pygments/formatters/rtf.py +147 -0
- wandb/vendor/pygments/formatters/svg.py +153 -0
- wandb/vendor/pygments/formatters/terminal.py +136 -0
- wandb/vendor/pygments/formatters/terminal256.py +309 -0
- wandb/vendor/pygments/lexer.py +871 -0
- wandb/vendor/pygments/lexers/__init__.py +329 -0
- wandb/vendor/pygments/lexers/_asy_builtins.py +1645 -0
- wandb/vendor/pygments/lexers/_cl_builtins.py +232 -0
- wandb/vendor/pygments/lexers/_cocoa_builtins.py +72 -0
- wandb/vendor/pygments/lexers/_csound_builtins.py +1346 -0
- wandb/vendor/pygments/lexers/_lasso_builtins.py +5327 -0
- wandb/vendor/pygments/lexers/_lua_builtins.py +295 -0
- wandb/vendor/pygments/lexers/_mapping.py +500 -0
- wandb/vendor/pygments/lexers/_mql_builtins.py +1172 -0
- wandb/vendor/pygments/lexers/_openedge_builtins.py +2547 -0
- wandb/vendor/pygments/lexers/_php_builtins.py +4756 -0
- wandb/vendor/pygments/lexers/_postgres_builtins.py +621 -0
- wandb/vendor/pygments/lexers/_scilab_builtins.py +3094 -0
- wandb/vendor/pygments/lexers/_sourcemod_builtins.py +1163 -0
- wandb/vendor/pygments/lexers/_stan_builtins.py +532 -0
- wandb/vendor/pygments/lexers/_stata_builtins.py +419 -0
- wandb/vendor/pygments/lexers/_tsql_builtins.py +1004 -0
- wandb/vendor/pygments/lexers/_vim_builtins.py +1939 -0
- wandb/vendor/pygments/lexers/actionscript.py +240 -0
- wandb/vendor/pygments/lexers/agile.py +24 -0
- wandb/vendor/pygments/lexers/algebra.py +221 -0
- wandb/vendor/pygments/lexers/ambient.py +76 -0
- wandb/vendor/pygments/lexers/ampl.py +87 -0
- wandb/vendor/pygments/lexers/apl.py +101 -0
- wandb/vendor/pygments/lexers/archetype.py +318 -0
- wandb/vendor/pygments/lexers/asm.py +641 -0
- wandb/vendor/pygments/lexers/automation.py +374 -0
- wandb/vendor/pygments/lexers/basic.py +500 -0
- wandb/vendor/pygments/lexers/bibtex.py +160 -0
- wandb/vendor/pygments/lexers/business.py +612 -0
- wandb/vendor/pygments/lexers/c_cpp.py +252 -0
- wandb/vendor/pygments/lexers/c_like.py +541 -0
- wandb/vendor/pygments/lexers/capnproto.py +78 -0
- wandb/vendor/pygments/lexers/chapel.py +102 -0
- wandb/vendor/pygments/lexers/clean.py +288 -0
- wandb/vendor/pygments/lexers/compiled.py +34 -0
- wandb/vendor/pygments/lexers/configs.py +833 -0
- wandb/vendor/pygments/lexers/console.py +114 -0
- wandb/vendor/pygments/lexers/crystal.py +393 -0
- wandb/vendor/pygments/lexers/csound.py +366 -0
- wandb/vendor/pygments/lexers/css.py +689 -0
- wandb/vendor/pygments/lexers/d.py +251 -0
- wandb/vendor/pygments/lexers/dalvik.py +125 -0
- wandb/vendor/pygments/lexers/data.py +555 -0
- wandb/vendor/pygments/lexers/diff.py +165 -0
- wandb/vendor/pygments/lexers/dotnet.py +691 -0
- wandb/vendor/pygments/lexers/dsls.py +878 -0
- wandb/vendor/pygments/lexers/dylan.py +289 -0
- wandb/vendor/pygments/lexers/ecl.py +125 -0
- wandb/vendor/pygments/lexers/eiffel.py +65 -0
- wandb/vendor/pygments/lexers/elm.py +121 -0
- wandb/vendor/pygments/lexers/erlang.py +533 -0
- wandb/vendor/pygments/lexers/esoteric.py +277 -0
- wandb/vendor/pygments/lexers/ezhil.py +69 -0
- wandb/vendor/pygments/lexers/factor.py +344 -0
- wandb/vendor/pygments/lexers/fantom.py +250 -0
- wandb/vendor/pygments/lexers/felix.py +273 -0
- wandb/vendor/pygments/lexers/forth.py +177 -0
- wandb/vendor/pygments/lexers/fortran.py +205 -0
- wandb/vendor/pygments/lexers/foxpro.py +428 -0
- wandb/vendor/pygments/lexers/functional.py +21 -0
- wandb/vendor/pygments/lexers/go.py +101 -0
- wandb/vendor/pygments/lexers/grammar_notation.py +213 -0
- wandb/vendor/pygments/lexers/graph.py +80 -0
- wandb/vendor/pygments/lexers/graphics.py +553 -0
- wandb/vendor/pygments/lexers/haskell.py +843 -0
- wandb/vendor/pygments/lexers/haxe.py +936 -0
- wandb/vendor/pygments/lexers/hdl.py +382 -0
- wandb/vendor/pygments/lexers/hexdump.py +103 -0
- wandb/vendor/pygments/lexers/html.py +602 -0
- wandb/vendor/pygments/lexers/idl.py +270 -0
- wandb/vendor/pygments/lexers/igor.py +288 -0
- wandb/vendor/pygments/lexers/inferno.py +96 -0
- wandb/vendor/pygments/lexers/installers.py +322 -0
- wandb/vendor/pygments/lexers/int_fiction.py +1343 -0
- wandb/vendor/pygments/lexers/iolang.py +63 -0
- wandb/vendor/pygments/lexers/j.py +146 -0
- wandb/vendor/pygments/lexers/javascript.py +1525 -0
- wandb/vendor/pygments/lexers/julia.py +333 -0
- wandb/vendor/pygments/lexers/jvm.py +1573 -0
- wandb/vendor/pygments/lexers/lisp.py +2621 -0
- wandb/vendor/pygments/lexers/make.py +202 -0
- wandb/vendor/pygments/lexers/markup.py +595 -0
- wandb/vendor/pygments/lexers/math.py +21 -0
- wandb/vendor/pygments/lexers/matlab.py +663 -0
- wandb/vendor/pygments/lexers/ml.py +769 -0
- wandb/vendor/pygments/lexers/modeling.py +358 -0
- wandb/vendor/pygments/lexers/modula2.py +1561 -0
- wandb/vendor/pygments/lexers/monte.py +204 -0
- wandb/vendor/pygments/lexers/ncl.py +894 -0
- wandb/vendor/pygments/lexers/nimrod.py +159 -0
- wandb/vendor/pygments/lexers/nit.py +64 -0
- wandb/vendor/pygments/lexers/nix.py +136 -0
- wandb/vendor/pygments/lexers/oberon.py +105 -0
- wandb/vendor/pygments/lexers/objective.py +504 -0
- wandb/vendor/pygments/lexers/ooc.py +85 -0
- wandb/vendor/pygments/lexers/other.py +41 -0
- wandb/vendor/pygments/lexers/parasail.py +79 -0
- wandb/vendor/pygments/lexers/parsers.py +835 -0
- wandb/vendor/pygments/lexers/pascal.py +644 -0
- wandb/vendor/pygments/lexers/pawn.py +199 -0
- wandb/vendor/pygments/lexers/perl.py +620 -0
- wandb/vendor/pygments/lexers/php.py +267 -0
- wandb/vendor/pygments/lexers/praat.py +294 -0
- wandb/vendor/pygments/lexers/prolog.py +306 -0
- wandb/vendor/pygments/lexers/python.py +939 -0
- wandb/vendor/pygments/lexers/qvt.py +152 -0
- wandb/vendor/pygments/lexers/r.py +453 -0
- wandb/vendor/pygments/lexers/rdf.py +270 -0
- wandb/vendor/pygments/lexers/rebol.py +431 -0
- wandb/vendor/pygments/lexers/resource.py +85 -0
- wandb/vendor/pygments/lexers/rnc.py +67 -0
- wandb/vendor/pygments/lexers/roboconf.py +82 -0
- wandb/vendor/pygments/lexers/robotframework.py +560 -0
- wandb/vendor/pygments/lexers/ruby.py +519 -0
- wandb/vendor/pygments/lexers/rust.py +220 -0
- wandb/vendor/pygments/lexers/sas.py +228 -0
- wandb/vendor/pygments/lexers/scripting.py +1222 -0
- wandb/vendor/pygments/lexers/shell.py +794 -0
- wandb/vendor/pygments/lexers/smalltalk.py +195 -0
- wandb/vendor/pygments/lexers/smv.py +79 -0
- wandb/vendor/pygments/lexers/snobol.py +83 -0
- wandb/vendor/pygments/lexers/special.py +103 -0
- wandb/vendor/pygments/lexers/sql.py +681 -0
- wandb/vendor/pygments/lexers/stata.py +108 -0
- wandb/vendor/pygments/lexers/supercollider.py +90 -0
- wandb/vendor/pygments/lexers/tcl.py +145 -0
- wandb/vendor/pygments/lexers/templates.py +2283 -0
- wandb/vendor/pygments/lexers/testing.py +207 -0
- wandb/vendor/pygments/lexers/text.py +25 -0
- wandb/vendor/pygments/lexers/textedit.py +169 -0
- wandb/vendor/pygments/lexers/textfmts.py +297 -0
- wandb/vendor/pygments/lexers/theorem.py +458 -0
- wandb/vendor/pygments/lexers/trafficscript.py +54 -0
- wandb/vendor/pygments/lexers/typoscript.py +226 -0
- wandb/vendor/pygments/lexers/urbi.py +133 -0
- wandb/vendor/pygments/lexers/varnish.py +190 -0
- wandb/vendor/pygments/lexers/verification.py +111 -0
- wandb/vendor/pygments/lexers/web.py +24 -0
- wandb/vendor/pygments/lexers/webmisc.py +988 -0
- wandb/vendor/pygments/lexers/whiley.py +116 -0
- wandb/vendor/pygments/lexers/x10.py +69 -0
- wandb/vendor/pygments/modeline.py +44 -0
- wandb/vendor/pygments/plugin.py +68 -0
- wandb/vendor/pygments/regexopt.py +92 -0
- wandb/vendor/pygments/scanner.py +105 -0
- wandb/vendor/pygments/sphinxext.py +158 -0
- wandb/vendor/pygments/style.py +155 -0
- wandb/vendor/pygments/styles/__init__.py +80 -0
- wandb/vendor/pygments/styles/abap.py +29 -0
- wandb/vendor/pygments/styles/algol.py +63 -0
- wandb/vendor/pygments/styles/algol_nu.py +63 -0
- wandb/vendor/pygments/styles/arduino.py +98 -0
- wandb/vendor/pygments/styles/autumn.py +65 -0
- wandb/vendor/pygments/styles/borland.py +51 -0
- wandb/vendor/pygments/styles/bw.py +49 -0
- wandb/vendor/pygments/styles/colorful.py +81 -0
- wandb/vendor/pygments/styles/default.py +73 -0
- wandb/vendor/pygments/styles/emacs.py +72 -0
- wandb/vendor/pygments/styles/friendly.py +72 -0
- wandb/vendor/pygments/styles/fruity.py +42 -0
- wandb/vendor/pygments/styles/igor.py +29 -0
- wandb/vendor/pygments/styles/lovelace.py +97 -0
- wandb/vendor/pygments/styles/manni.py +75 -0
- wandb/vendor/pygments/styles/monokai.py +106 -0
- wandb/vendor/pygments/styles/murphy.py +80 -0
- wandb/vendor/pygments/styles/native.py +65 -0
- wandb/vendor/pygments/styles/paraiso_dark.py +125 -0
- wandb/vendor/pygments/styles/paraiso_light.py +125 -0
- wandb/vendor/pygments/styles/pastie.py +75 -0
- wandb/vendor/pygments/styles/perldoc.py +69 -0
- wandb/vendor/pygments/styles/rainbow_dash.py +89 -0
- wandb/vendor/pygments/styles/rrt.py +33 -0
- wandb/vendor/pygments/styles/sas.py +44 -0
- wandb/vendor/pygments/styles/stata.py +40 -0
- wandb/vendor/pygments/styles/tango.py +141 -0
- wandb/vendor/pygments/styles/trac.py +63 -0
- wandb/vendor/pygments/styles/vim.py +63 -0
- wandb/vendor/pygments/styles/vs.py +38 -0
- wandb/vendor/pygments/styles/xcode.py +51 -0
- wandb/vendor/pygments/token.py +213 -0
- wandb/vendor/pygments/unistring.py +217 -0
- wandb/vendor/pygments/util.py +388 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/__init__.py +17 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/events.py +615 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/__init__.py +98 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/api.py +369 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/fsevents.py +172 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/fsevents2.py +239 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/inotify.py +218 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/inotify_buffer.py +81 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/inotify_c.py +575 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/kqueue.py +730 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/polling.py +145 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/read_directory_changes.py +133 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/winapi.py +348 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/patterns.py +265 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/tricks/__init__.py +174 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/__init__.py +151 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/bricks.py +249 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/compat.py +29 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/decorators.py +198 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/delayed_queue.py +88 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/dirsnapshot.py +293 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/echo.py +157 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/event_backport.py +41 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/importlib2.py +40 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/platform.py +57 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/unicode_paths.py +64 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/win32stat.py +123 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/version.py +28 -0
- wandb/vendor/watchdog_0_9_0/wandb_watchdog/watchmedo.py +577 -0
- wandb/wandb_agent.py +580 -0
- wandb/wandb_controller.py +719 -0
- wandb/wandb_run.py +8 -0
- wandb-0.21.2.dist-info/METADATA +223 -0
- wandb-0.21.2.dist-info/RECORD +904 -0
- wandb-0.21.2.dist-info/WHEEL +4 -0
- wandb-0.21.2.dist-info/entry_points.txt +3 -0
- wandb-0.21.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1468 @@
|
|
1
|
+
import base64
|
2
|
+
import binascii
|
3
|
+
import codecs
|
4
|
+
import datetime
|
5
|
+
import json
|
6
|
+
import logging
|
7
|
+
import os
|
8
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Set, Tuple
|
9
|
+
|
10
|
+
import wandb
|
11
|
+
from wandb import util
|
12
|
+
from wandb.sdk.lib import runid
|
13
|
+
|
14
|
+
from . import _dtypes
|
15
|
+
from ._private import MEDIA_TMP
|
16
|
+
from .base_types.media import Media, _numpy_arrays_to_lists
|
17
|
+
from .base_types.wb_value import WBValue
|
18
|
+
from .table_decorators import (
|
19
|
+
allow_incremental_logging_after_append,
|
20
|
+
allow_relogging_after_mutation,
|
21
|
+
ensure_not_incremental,
|
22
|
+
)
|
23
|
+
from .utils import _json_helper
|
24
|
+
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from wandb.sdk.artifacts import artifact
|
27
|
+
|
28
|
+
from ...wandb_run import Run as LocalRun
|
29
|
+
|
30
|
+
|
31
|
+
class _TableLinkMixin:
|
32
|
+
def set_table(self, table):
|
33
|
+
self._table = table
|
34
|
+
|
35
|
+
|
36
|
+
class _TableKey(str, _TableLinkMixin):
|
37
|
+
def set_table(self, table, col_name):
|
38
|
+
assert col_name in table.columns
|
39
|
+
self._table = table
|
40
|
+
self._col_name = col_name
|
41
|
+
|
42
|
+
|
43
|
+
class _TableIndex(int, _TableLinkMixin):
|
44
|
+
def get_row(self):
|
45
|
+
row = {}
|
46
|
+
if self._table:
|
47
|
+
row = {
|
48
|
+
c: self._table.data[self][i] for i, c in enumerate(self._table.columns)
|
49
|
+
}
|
50
|
+
|
51
|
+
return row
|
52
|
+
|
53
|
+
|
54
|
+
class _PrimaryKeyType(_dtypes.Type):
|
55
|
+
name = "primaryKey"
|
56
|
+
legacy_names = ["wandb.TablePrimaryKey"]
|
57
|
+
|
58
|
+
def assign_type(self, wb_type=None):
|
59
|
+
if isinstance(wb_type, _dtypes.StringType) or isinstance(
|
60
|
+
wb_type, _PrimaryKeyType
|
61
|
+
):
|
62
|
+
return self
|
63
|
+
return _dtypes.InvalidType()
|
64
|
+
|
65
|
+
@classmethod
|
66
|
+
def from_obj(cls, py_obj):
|
67
|
+
if not isinstance(py_obj, _TableKey):
|
68
|
+
raise TypeError("py_obj must be a wandb.Table")
|
69
|
+
else:
|
70
|
+
return cls()
|
71
|
+
|
72
|
+
|
73
|
+
class _ForeignKeyType(_dtypes.Type):
|
74
|
+
name = "foreignKey"
|
75
|
+
legacy_names = ["wandb.TableForeignKey"]
|
76
|
+
types = [_TableKey]
|
77
|
+
|
78
|
+
def __init__(self, table, col_name):
|
79
|
+
assert isinstance(table, Table)
|
80
|
+
assert isinstance(col_name, str)
|
81
|
+
assert col_name in table.columns
|
82
|
+
self.params.update({"table": table, "col_name": col_name})
|
83
|
+
|
84
|
+
def assign_type(self, wb_type=None):
|
85
|
+
if isinstance(wb_type, _dtypes.StringType):
|
86
|
+
return self
|
87
|
+
elif (
|
88
|
+
isinstance(wb_type, _ForeignKeyType)
|
89
|
+
and id(self.params["table"]) == id(wb_type.params["table"])
|
90
|
+
and self.params["col_name"] == wb_type.params["col_name"]
|
91
|
+
):
|
92
|
+
return self
|
93
|
+
|
94
|
+
return _dtypes.InvalidType()
|
95
|
+
|
96
|
+
@classmethod
|
97
|
+
def from_obj(cls, py_obj):
|
98
|
+
if not isinstance(py_obj, _TableKey):
|
99
|
+
raise TypeError("py_obj must be a _TableKey")
|
100
|
+
else:
|
101
|
+
return cls(py_obj._table, py_obj._col_name)
|
102
|
+
|
103
|
+
def to_json(self, artifact=None):
|
104
|
+
res = super().to_json(artifact)
|
105
|
+
if artifact is not None:
|
106
|
+
table_name = f"media/tables/t_{runid.generate_id()}"
|
107
|
+
entry = artifact.add(self.params["table"], table_name)
|
108
|
+
res["params"]["table"] = entry.path
|
109
|
+
else:
|
110
|
+
raise AssertionError(
|
111
|
+
"_ForeignKeyType does not support serialization without an artifact"
|
112
|
+
)
|
113
|
+
return res
|
114
|
+
|
115
|
+
@classmethod
|
116
|
+
def from_json(
|
117
|
+
cls,
|
118
|
+
json_dict,
|
119
|
+
artifact,
|
120
|
+
):
|
121
|
+
table = None
|
122
|
+
col_name = None
|
123
|
+
if artifact is None:
|
124
|
+
raise AssertionError(
|
125
|
+
"_ForeignKeyType does not support deserialization without an artifact"
|
126
|
+
)
|
127
|
+
else:
|
128
|
+
table = artifact.get(json_dict["params"]["table"])
|
129
|
+
col_name = json_dict["params"]["col_name"]
|
130
|
+
|
131
|
+
if table is None:
|
132
|
+
raise AssertionError("Unable to deserialize referenced table")
|
133
|
+
|
134
|
+
return cls(table, col_name)
|
135
|
+
|
136
|
+
|
137
|
+
class _ForeignIndexType(_dtypes.Type):
|
138
|
+
name = "foreignIndex"
|
139
|
+
legacy_names = ["wandb.TableForeignIndex"]
|
140
|
+
types = [_TableIndex]
|
141
|
+
|
142
|
+
def __init__(self, table):
|
143
|
+
assert isinstance(table, Table)
|
144
|
+
self.params.update({"table": table})
|
145
|
+
|
146
|
+
def assign_type(self, wb_type=None):
|
147
|
+
if isinstance(wb_type, _dtypes.NumberType):
|
148
|
+
return self
|
149
|
+
elif isinstance(wb_type, _ForeignIndexType) and id(self.params["table"]) == id(
|
150
|
+
wb_type.params["table"]
|
151
|
+
):
|
152
|
+
return self
|
153
|
+
|
154
|
+
return _dtypes.InvalidType()
|
155
|
+
|
156
|
+
@classmethod
|
157
|
+
def from_obj(cls, py_obj):
|
158
|
+
if not isinstance(py_obj, _TableIndex):
|
159
|
+
raise TypeError("py_obj must be a _TableIndex")
|
160
|
+
else:
|
161
|
+
return cls(py_obj._table)
|
162
|
+
|
163
|
+
def to_json(self, artifact=None):
|
164
|
+
res = super().to_json(artifact)
|
165
|
+
if artifact is not None:
|
166
|
+
table_name = f"media/tables/t_{runid.generate_id()}"
|
167
|
+
entry = artifact.add(self.params["table"], table_name)
|
168
|
+
res["params"]["table"] = entry.path
|
169
|
+
else:
|
170
|
+
raise AssertionError(
|
171
|
+
"_ForeignIndexType does not support serialization without an artifact"
|
172
|
+
)
|
173
|
+
return res
|
174
|
+
|
175
|
+
@classmethod
|
176
|
+
def from_json(
|
177
|
+
cls,
|
178
|
+
json_dict,
|
179
|
+
artifact,
|
180
|
+
):
|
181
|
+
table = None
|
182
|
+
if artifact is None:
|
183
|
+
raise AssertionError(
|
184
|
+
"_ForeignIndexType does not support deserialization without an artifact"
|
185
|
+
)
|
186
|
+
else:
|
187
|
+
table = artifact.get(json_dict["params"]["table"])
|
188
|
+
|
189
|
+
if table is None:
|
190
|
+
raise AssertionError("Unable to deserialize referenced table")
|
191
|
+
|
192
|
+
return cls(table)
|
193
|
+
|
194
|
+
|
195
|
+
_SUPPORTED_LOGGING_MODES = ["IMMUTABLE", "MUTABLE", "INCREMENTAL"]
|
196
|
+
|
197
|
+
|
198
|
+
class Table(Media):
|
199
|
+
"""The Table class used to display and analyze tabular data.
|
200
|
+
|
201
|
+
Unlike traditional spreadsheets, Tables support numerous types of data:
|
202
|
+
scalar values, strings, numpy arrays, and most subclasses of `wandb.data_types.Media`.
|
203
|
+
This means you can embed `Images`, `Video`, `Audio`, and other sorts of rich, annotated media
|
204
|
+
directly in Tables, alongside other traditional scalar values.
|
205
|
+
|
206
|
+
This class is the primary class used to generate W&B Tables
|
207
|
+
https://docs.wandb.ai/guides/models/tables/.
|
208
|
+
"""
|
209
|
+
|
210
|
+
MAX_ROWS = 10000
|
211
|
+
MAX_ARTIFACT_ROWS = 200000
|
212
|
+
_MAX_EMBEDDING_DIMENSIONS = 150
|
213
|
+
_log_type = "table"
|
214
|
+
|
215
|
+
def __init__(
|
216
|
+
self,
|
217
|
+
columns=None,
|
218
|
+
data=None,
|
219
|
+
rows=None,
|
220
|
+
dataframe=None,
|
221
|
+
dtype=None,
|
222
|
+
optional=True,
|
223
|
+
allow_mixed_types=False,
|
224
|
+
log_mode: Optional[
|
225
|
+
Literal["IMMUTABLE", "MUTABLE", "INCREMENTAL"]
|
226
|
+
] = "IMMUTABLE",
|
227
|
+
):
|
228
|
+
"""Initializes a Table object.
|
229
|
+
|
230
|
+
The rows is available for legacy reasons and should not be used.
|
231
|
+
The Table class uses data to mimic the Pandas API.
|
232
|
+
|
233
|
+
Args:
|
234
|
+
columns: (List[str]) Names of the columns in the table.
|
235
|
+
Defaults to ["Input", "Output", "Expected"].
|
236
|
+
data: (List[List[any]]) 2D row-oriented array of values.
|
237
|
+
dataframe: (pandas.DataFrame) DataFrame object used to create the table.
|
238
|
+
When set, `data` and `columns` arguments are ignored.
|
239
|
+
rows: (List[List[any]]) 2D row-oriented array of values.
|
240
|
+
optional: (Union[bool,List[bool]]) Determines if `None` values are allowed. Default to True
|
241
|
+
- If a singular bool value, then the optionality is enforced for all
|
242
|
+
columns specified at construction time
|
243
|
+
- If a list of bool values, then the optionality is applied to each
|
244
|
+
column - should be the same length as `columns`
|
245
|
+
applies to all columns. A list of bool values applies to each respective column.
|
246
|
+
allow_mixed_types: (bool) Determines if columns are allowed to have mixed types
|
247
|
+
(disables type validation). Defaults to False
|
248
|
+
log_mode: Optional[str] Controls how the Table is logged when mutations occur.
|
249
|
+
Options:
|
250
|
+
- "IMMUTABLE" (default): Table can only be logged once; subsequent
|
251
|
+
logging attempts after the table has been mutated will be no-ops.
|
252
|
+
- "MUTABLE": Table can be re-logged after mutations, creating
|
253
|
+
a new artifact version each time it's logged.
|
254
|
+
- "INCREMENTAL": Table data is logged incrementally, with each log creating
|
255
|
+
a new artifact entry containing the new data since the last log.
|
256
|
+
"""
|
257
|
+
super().__init__()
|
258
|
+
self._validate_log_mode(log_mode)
|
259
|
+
self.log_mode = log_mode
|
260
|
+
if self.log_mode == "INCREMENTAL":
|
261
|
+
self._increment_num: int | None = None
|
262
|
+
self._last_logged_idx: int | None = None
|
263
|
+
self._previous_increments_paths: list[str] | None = None
|
264
|
+
self._run_target_for_increments: LocalRun | None = None
|
265
|
+
self._pk_col = None
|
266
|
+
self._fk_cols: set[str] = set()
|
267
|
+
if allow_mixed_types:
|
268
|
+
dtype = _dtypes.AnyType
|
269
|
+
|
270
|
+
# This is kept for legacy reasons (tss: personally, I think we should remove this)
|
271
|
+
if columns is None:
|
272
|
+
columns = ["Input", "Output", "Expected"]
|
273
|
+
|
274
|
+
# Explicit dataframe option
|
275
|
+
if dataframe is not None:
|
276
|
+
self._init_from_dataframe(dataframe, columns, optional, dtype)
|
277
|
+
else:
|
278
|
+
# Expected pattern
|
279
|
+
if data is not None:
|
280
|
+
if util.is_numpy_array(data):
|
281
|
+
self._init_from_ndarray(data, columns, optional, dtype)
|
282
|
+
elif util.is_pandas_data_frame(data):
|
283
|
+
self._init_from_dataframe(data, columns, optional, dtype)
|
284
|
+
else:
|
285
|
+
self._init_from_list(data, columns, optional, dtype)
|
286
|
+
|
287
|
+
# legacy
|
288
|
+
elif rows is not None:
|
289
|
+
self._init_from_list(rows, columns, optional, dtype)
|
290
|
+
|
291
|
+
# Default empty case
|
292
|
+
else:
|
293
|
+
self._init_from_list([], columns, optional, dtype)
|
294
|
+
|
295
|
+
def _validate_log_mode(self, log_mode):
|
296
|
+
assert log_mode in _SUPPORTED_LOGGING_MODES, (
|
297
|
+
f"Invalid log_mode: {log_mode}. Must be one of {_SUPPORTED_LOGGING_MODES}"
|
298
|
+
)
|
299
|
+
|
300
|
+
@staticmethod
|
301
|
+
def _assert_valid_columns(columns):
|
302
|
+
valid_col_types = [str, int]
|
303
|
+
assert isinstance(columns, list), "columns argument expects a `list` object"
|
304
|
+
assert len(columns) == 0 or all(
|
305
|
+
[type(col) in valid_col_types for col in columns]
|
306
|
+
), "columns argument expects list of strings or ints"
|
307
|
+
|
308
|
+
def _init_from_list(self, data, columns, optional=True, dtype=None):
|
309
|
+
assert isinstance(data, list), "data argument expects a `list` object"
|
310
|
+
self.data = []
|
311
|
+
self._assert_valid_columns(columns)
|
312
|
+
self.columns = columns
|
313
|
+
self._make_column_types(dtype, optional)
|
314
|
+
for row in data:
|
315
|
+
self.add_data(*row)
|
316
|
+
|
317
|
+
def _init_from_ndarray(self, ndarray, columns, optional=True, dtype=None):
|
318
|
+
assert util.is_numpy_array(ndarray), (
|
319
|
+
"ndarray argument expects a `numpy.ndarray` object"
|
320
|
+
)
|
321
|
+
self.data = []
|
322
|
+
self._assert_valid_columns(columns)
|
323
|
+
self.columns = columns
|
324
|
+
self._make_column_types(dtype, optional)
|
325
|
+
for row in ndarray:
|
326
|
+
self.add_data(*row)
|
327
|
+
|
328
|
+
def _init_from_dataframe(self, dataframe, columns, optional=True, dtype=None):
|
329
|
+
assert util.is_pandas_data_frame(dataframe), (
|
330
|
+
"dataframe argument expects a `pandas.core.frame.DataFrame` object"
|
331
|
+
)
|
332
|
+
self.data = []
|
333
|
+
columns = list(dataframe.columns)
|
334
|
+
self._assert_valid_columns(columns)
|
335
|
+
self.columns = columns
|
336
|
+
self._make_column_types(dtype, optional)
|
337
|
+
for row in range(len(dataframe)):
|
338
|
+
self.add_data(*tuple(dataframe[col].values[row] for col in self.columns))
|
339
|
+
|
340
|
+
def _make_column_types(self, dtype=None, optional=True):
|
341
|
+
if dtype is None:
|
342
|
+
dtype = _dtypes.UnknownType()
|
343
|
+
|
344
|
+
if optional.__class__ is not list:
|
345
|
+
optional = [optional for _ in range(len(self.columns))]
|
346
|
+
|
347
|
+
if dtype.__class__ is not list:
|
348
|
+
dtype = [dtype for _ in range(len(self.columns))]
|
349
|
+
|
350
|
+
self._column_types = _dtypes.TypedDictType({})
|
351
|
+
for col_name, opt, dt in zip(self.columns, optional, dtype):
|
352
|
+
self.cast(col_name, dt, opt)
|
353
|
+
|
354
|
+
def _load_incremental_table_state_from_resumed_run(self, run: "LocalRun", key: str):
|
355
|
+
"""Handle updating incremental table state for resumed runs.
|
356
|
+
|
357
|
+
This method is called when a run is resumed and there are previous
|
358
|
+
increments of this table that need to be preserved. It updates the
|
359
|
+
table's internal state to track previous increments and the current
|
360
|
+
increment number.
|
361
|
+
"""
|
362
|
+
if (
|
363
|
+
self._previous_increments_paths is not None
|
364
|
+
or self._increment_num is not None
|
365
|
+
):
|
366
|
+
raise AssertionError(
|
367
|
+
"The table has been initialized for a resumed run already"
|
368
|
+
)
|
369
|
+
|
370
|
+
self._set_incremental_table_run_target(run)
|
371
|
+
|
372
|
+
summary_from_key = run.summary.get(key)
|
373
|
+
|
374
|
+
if (
|
375
|
+
summary_from_key is None
|
376
|
+
or not isinstance(summary_from_key, dict)
|
377
|
+
or summary_from_key.get("_type") != "incremental-table-file"
|
378
|
+
):
|
379
|
+
# The key was never logged to the run or its last logged
|
380
|
+
# value was not an incrementally logged table.
|
381
|
+
return
|
382
|
+
|
383
|
+
previous_increments_paths = summary_from_key.get(
|
384
|
+
"previous_increments_paths", []
|
385
|
+
)
|
386
|
+
|
387
|
+
# add the artifact path of the last logged increment
|
388
|
+
last_artifact_path = summary_from_key.get("artifact_path")
|
389
|
+
|
390
|
+
if last_artifact_path:
|
391
|
+
previous_increments_paths.append(last_artifact_path)
|
392
|
+
|
393
|
+
# add 1 because a new increment is being logged
|
394
|
+
last_increment_num = summary_from_key.get("increment_num", 0)
|
395
|
+
|
396
|
+
self._increment_num = last_increment_num + 1
|
397
|
+
self._previous_increments_paths = previous_increments_paths
|
398
|
+
|
399
|
+
def _set_incremental_table_run_target(self, run: "LocalRun") -> None:
|
400
|
+
"""Associate a Run object with this incremental Table.
|
401
|
+
|
402
|
+
A Table object in incremental mode can only be logged to a single Run.
|
403
|
+
Raises an error if the table is already associated to a different run.
|
404
|
+
"""
|
405
|
+
if self._run_target_for_increments is None:
|
406
|
+
self._run_target_for_increments = run
|
407
|
+
elif self._run_target_for_increments is not run:
|
408
|
+
raise AssertionError("An incremental Table can only be logged to one Run.")
|
409
|
+
|
410
|
+
@allow_relogging_after_mutation
|
411
|
+
def cast(self, col_name, dtype, optional=False):
|
412
|
+
"""Casts a column to a specific data type.
|
413
|
+
|
414
|
+
This can be one of the normal python classes, an internal W&B type,
|
415
|
+
or an example object, like an instance of wandb.Image or
|
416
|
+
wandb.Classes.
|
417
|
+
|
418
|
+
Args:
|
419
|
+
col_name (str): The name of the column to cast.
|
420
|
+
dtype (class, wandb.wandb_sdk.interface._dtypes.Type, any): The
|
421
|
+
target dtype.
|
422
|
+
optional (bool): If the column should allow Nones.
|
423
|
+
"""
|
424
|
+
assert col_name in self.columns
|
425
|
+
|
426
|
+
wbtype = _dtypes.TypeRegistry.type_from_dtype(dtype)
|
427
|
+
|
428
|
+
if optional:
|
429
|
+
wbtype = _dtypes.OptionalType(wbtype)
|
430
|
+
|
431
|
+
# Cast each value in the row, raising an error if there are invalid entries.
|
432
|
+
col_ndx = self.columns.index(col_name)
|
433
|
+
for row in self.data:
|
434
|
+
result_type = wbtype.assign(row[col_ndx])
|
435
|
+
if isinstance(result_type, _dtypes.InvalidType):
|
436
|
+
raise TypeError(
|
437
|
+
f"Existing data {row[col_ndx]}, of type {_dtypes.TypeRegistry.type_of(row[col_ndx])} cannot be cast to {wbtype}"
|
438
|
+
)
|
439
|
+
wbtype = result_type
|
440
|
+
|
441
|
+
# Assert valid options
|
442
|
+
is_pk = isinstance(wbtype, _PrimaryKeyType)
|
443
|
+
is_fk = isinstance(wbtype, _ForeignKeyType)
|
444
|
+
is_fi = isinstance(wbtype, _ForeignIndexType)
|
445
|
+
if is_pk or is_fk or is_fi:
|
446
|
+
assert not optional, (
|
447
|
+
"Primary keys, foreign keys, and foreign indexes cannot be optional."
|
448
|
+
)
|
449
|
+
|
450
|
+
if (is_fk or is_fk) and id(wbtype.params["table"]) == id(self):
|
451
|
+
raise AssertionError("Cannot set a foreign table reference to same table.")
|
452
|
+
|
453
|
+
if is_pk:
|
454
|
+
assert self._pk_col is None, (
|
455
|
+
f"Cannot have multiple primary keys - {self._pk_col} is already set as the primary key."
|
456
|
+
)
|
457
|
+
|
458
|
+
# Update the column type
|
459
|
+
self._column_types.params["type_map"][col_name] = wbtype
|
460
|
+
|
461
|
+
# Wrap the data if needed
|
462
|
+
self._update_keys()
|
463
|
+
return wbtype
|
464
|
+
|
465
|
+
def __ne__(self, other):
|
466
|
+
return not self.__eq__(other)
|
467
|
+
|
468
|
+
def _eq_debug(self, other, should_assert=False):
|
469
|
+
eq = isinstance(other, Table)
|
470
|
+
assert not should_assert or eq, (
|
471
|
+
f"Found type {other.__class__}, expected {Table}"
|
472
|
+
)
|
473
|
+
eq = eq and len(self.data) == len(other.data)
|
474
|
+
assert not should_assert or eq, (
|
475
|
+
f"Found {len(other.data)} rows, expected {len(self.data)}"
|
476
|
+
)
|
477
|
+
eq = eq and self.columns == other.columns
|
478
|
+
assert not should_assert or eq, (
|
479
|
+
f"Found columns {other.columns}, expected {self.columns}"
|
480
|
+
)
|
481
|
+
eq = eq and self._column_types == other._column_types
|
482
|
+
assert not should_assert or eq, (
|
483
|
+
f"Found column type {other._column_types}, expected column type {self._column_types}"
|
484
|
+
)
|
485
|
+
if eq:
|
486
|
+
for row_ndx in range(len(self.data)):
|
487
|
+
for col_ndx in range(len(self.data[row_ndx])):
|
488
|
+
_eq = self.data[row_ndx][col_ndx] == other.data[row_ndx][col_ndx]
|
489
|
+
# equal if all are equal
|
490
|
+
if util.is_numpy_array(_eq):
|
491
|
+
_eq = ((_eq * -1) + 1).sum() == 0
|
492
|
+
eq = eq and _eq
|
493
|
+
assert not should_assert or eq, (
|
494
|
+
f"Unequal data at row_ndx {row_ndx} col_ndx {col_ndx}: found {other.data[row_ndx][col_ndx]}, expected {self.data[row_ndx][col_ndx]}"
|
495
|
+
)
|
496
|
+
if not eq:
|
497
|
+
return eq
|
498
|
+
return eq
|
499
|
+
|
500
|
+
def __eq__(self, other):
|
501
|
+
return self._eq_debug(other)
|
502
|
+
|
503
|
+
@allow_relogging_after_mutation
|
504
|
+
def add_row(self, *row):
|
505
|
+
"""Deprecated. Use `Table.add_data` method instead."""
|
506
|
+
logging.warning("add_row is deprecated, use add_data")
|
507
|
+
self.add_data(*row)
|
508
|
+
|
509
|
+
@allow_relogging_after_mutation
|
510
|
+
@allow_incremental_logging_after_append
|
511
|
+
def add_data(self, *data):
|
512
|
+
"""Adds a new row of data to the table.
|
513
|
+
|
514
|
+
The maximum amount ofrows in a table is determined by
|
515
|
+
`wandb.Table.MAX_ARTIFACT_ROWS`.
|
516
|
+
|
517
|
+
The length of the data should match the length of the table column.
|
518
|
+
"""
|
519
|
+
if len(data) != len(self.columns):
|
520
|
+
raise ValueError(
|
521
|
+
f"This table expects {len(self.columns)} columns: {self.columns}, found {len(data)}"
|
522
|
+
)
|
523
|
+
|
524
|
+
# Special case to pre-emptively cast a column as a key.
|
525
|
+
# Needed as String.assign(Key) is invalid
|
526
|
+
for ndx, item in enumerate(data):
|
527
|
+
if isinstance(item, _TableLinkMixin):
|
528
|
+
self.cast(
|
529
|
+
self.columns[ndx],
|
530
|
+
_dtypes.TypeRegistry.type_of(item),
|
531
|
+
optional=False,
|
532
|
+
)
|
533
|
+
|
534
|
+
# Update the table's column types
|
535
|
+
result_type = self._get_updated_result_type(data)
|
536
|
+
self._column_types = result_type
|
537
|
+
|
538
|
+
# rows need to be mutable
|
539
|
+
if isinstance(data, tuple):
|
540
|
+
data = list(data)
|
541
|
+
# Add the new data
|
542
|
+
self.data.append(data)
|
543
|
+
|
544
|
+
# Update the wrapper values if needed
|
545
|
+
self._update_keys(force_last=True)
|
546
|
+
|
547
|
+
def _get_updated_result_type(self, row):
|
548
|
+
"""Returns the updated result type based on the inputted row.
|
549
|
+
|
550
|
+
Raises:
|
551
|
+
TypeError: if the assignment is invalid.
|
552
|
+
"""
|
553
|
+
incoming_row_dict = {
|
554
|
+
col_key: row[ndx] for ndx, col_key in enumerate(self.columns)
|
555
|
+
}
|
556
|
+
current_type = self._column_types
|
557
|
+
result_type = current_type.assign(incoming_row_dict)
|
558
|
+
if isinstance(result_type, _dtypes.InvalidType):
|
559
|
+
raise TypeError(
|
560
|
+
f"Data row contained incompatible types:\n{current_type.explain(incoming_row_dict)}"
|
561
|
+
)
|
562
|
+
return result_type
|
563
|
+
|
564
|
+
def _to_table_json(self, max_rows=None, warn=True):
|
565
|
+
# separate this method for easier testing
|
566
|
+
if max_rows is None:
|
567
|
+
max_rows = Table.MAX_ROWS
|
568
|
+
n_rows = len(self.data)
|
569
|
+
if n_rows > max_rows and warn:
|
570
|
+
# NOTE: Never raises for reinit="create_new" runs.
|
571
|
+
# Since this is called by bind_to_run(), this can be fixed by
|
572
|
+
# propagating the run. It cannot be fixed for to_json() calls
|
573
|
+
# that are given an artifact, other than by deferring to singleton
|
574
|
+
# settings.
|
575
|
+
if wandb.run and (
|
576
|
+
wandb.run.settings.table_raise_on_max_row_limit_exceeded
|
577
|
+
or wandb.run.settings.strict
|
578
|
+
):
|
579
|
+
raise ValueError(
|
580
|
+
f"Table row limit exceeded: table has {n_rows} rows, limit is {max_rows}. "
|
581
|
+
f"To increase the maximum number of allowed rows in a wandb.Table, override "
|
582
|
+
f"the limit with `wandb.Table.MAX_ARTIFACT_ROWS = X` and try again. Note: "
|
583
|
+
f"this may cause slower queries in the W&B UI."
|
584
|
+
)
|
585
|
+
logging.warning(f"Truncating wandb.Table object to {max_rows} rows.")
|
586
|
+
|
587
|
+
if self.log_mode == "INCREMENTAL" and self._last_logged_idx is not None:
|
588
|
+
return {
|
589
|
+
"columns": self.columns,
|
590
|
+
"data": self.data[
|
591
|
+
self._last_logged_idx + 1 : self._last_logged_idx + 1 + max_rows
|
592
|
+
],
|
593
|
+
}
|
594
|
+
else:
|
595
|
+
return {"columns": self.columns, "data": self.data[:max_rows]}
|
596
|
+
|
597
|
+
def bind_to_run(self, *args, **kwargs):
|
598
|
+
"""Bind this object to a run.
|
599
|
+
|
600
|
+
<!-- lazydoc-ignore: internal -->
|
601
|
+
"""
|
602
|
+
# We set `warn=False` since Tables will now always be logged to both
|
603
|
+
# files and artifacts. The file limit will never practically matter and
|
604
|
+
# this code path will be ultimately removed. The 10k limit warning confuses
|
605
|
+
# users given that we publicly say 200k is the limit.
|
606
|
+
data = self._to_table_json(warn=False)
|
607
|
+
tmp_path = os.path.join(MEDIA_TMP.name, runid.generate_id() + ".table.json")
|
608
|
+
data = _numpy_arrays_to_lists(data)
|
609
|
+
with codecs.open(tmp_path, "w", encoding="utf-8") as fp:
|
610
|
+
util.json_dump_safer(data, fp)
|
611
|
+
self._set_file(tmp_path, is_tmp=True, extension=".table.json")
|
612
|
+
super().bind_to_run(*args, **kwargs)
|
613
|
+
|
614
|
+
@classmethod
|
615
|
+
def get_media_subdir(cls):
|
616
|
+
"""Get media subdirectory.
|
617
|
+
|
618
|
+
<!-- lazydoc-ignore-classmethod: internal -->
|
619
|
+
"""
|
620
|
+
return os.path.join("media", "table")
|
621
|
+
|
622
|
+
@classmethod
|
623
|
+
def from_json(cls, json_obj, source_artifact: "artifact.Artifact"):
|
624
|
+
"""Deserialize JSON object into it's class representation.
|
625
|
+
|
626
|
+
<!-- lazydoc-ignore-classmethod: internal -->
|
627
|
+
"""
|
628
|
+
data = []
|
629
|
+
column_types = None
|
630
|
+
np_deserialized_columns = {}
|
631
|
+
timestamp_column_indices = set()
|
632
|
+
log_mode = json_obj.get("log_mode", "IMMUTABLE")
|
633
|
+
if json_obj.get("column_types") is not None:
|
634
|
+
column_types = _dtypes.TypeRegistry.type_from_dict(
|
635
|
+
json_obj["column_types"], source_artifact
|
636
|
+
)
|
637
|
+
for col_name in column_types.params["type_map"]:
|
638
|
+
col_type = column_types.params["type_map"][col_name]
|
639
|
+
ndarray_type = None
|
640
|
+
if isinstance(col_type, _dtypes.NDArrayType):
|
641
|
+
ndarray_type = col_type
|
642
|
+
elif isinstance(col_type, _dtypes.UnionType):
|
643
|
+
for t in col_type.params["allowed_types"]:
|
644
|
+
if isinstance(t, _dtypes.NDArrayType):
|
645
|
+
ndarray_type = t
|
646
|
+
elif isinstance(t, _dtypes.TimestampType):
|
647
|
+
timestamp_column_indices.add(
|
648
|
+
json_obj["columns"].index(col_name)
|
649
|
+
)
|
650
|
+
|
651
|
+
elif isinstance(col_type, _dtypes.TimestampType):
|
652
|
+
timestamp_column_indices.add(json_obj["columns"].index(col_name))
|
653
|
+
|
654
|
+
if (
|
655
|
+
ndarray_type is not None
|
656
|
+
and ndarray_type._get_serialization_path() is not None
|
657
|
+
):
|
658
|
+
serialization_path = ndarray_type._get_serialization_path()
|
659
|
+
|
660
|
+
if serialization_path is None:
|
661
|
+
continue
|
662
|
+
|
663
|
+
np = util.get_module(
|
664
|
+
"numpy",
|
665
|
+
required="Deserializing NumPy columns requires NumPy to be installed.",
|
666
|
+
)
|
667
|
+
deserialized = np.load(
|
668
|
+
source_artifact.get_entry(serialization_path["path"]).download()
|
669
|
+
)
|
670
|
+
np_deserialized_columns[json_obj["columns"].index(col_name)] = (
|
671
|
+
deserialized[serialization_path["key"]]
|
672
|
+
)
|
673
|
+
ndarray_type._clear_serialization_path()
|
674
|
+
|
675
|
+
if log_mode == "INCREMENTAL":
|
676
|
+
unprocessed_table_data = _get_data_from_increments(
|
677
|
+
json_obj, source_artifact
|
678
|
+
)
|
679
|
+
else:
|
680
|
+
unprocessed_table_data = json_obj["data"]
|
681
|
+
|
682
|
+
for r_ndx, row in enumerate(unprocessed_table_data):
|
683
|
+
data.append(
|
684
|
+
_process_table_row(
|
685
|
+
row,
|
686
|
+
timestamp_column_indices,
|
687
|
+
np_deserialized_columns,
|
688
|
+
source_artifact,
|
689
|
+
r_ndx,
|
690
|
+
)
|
691
|
+
)
|
692
|
+
|
693
|
+
# construct Table with dtypes for each column if type information exists
|
694
|
+
dtypes = None
|
695
|
+
if column_types is not None:
|
696
|
+
dtypes = [
|
697
|
+
column_types.params["type_map"][str(col)] for col in json_obj["columns"]
|
698
|
+
]
|
699
|
+
|
700
|
+
new_obj = cls(
|
701
|
+
columns=json_obj["columns"], data=data, dtype=dtypes, log_mode=log_mode
|
702
|
+
)
|
703
|
+
|
704
|
+
if column_types is not None:
|
705
|
+
new_obj._column_types = column_types
|
706
|
+
|
707
|
+
new_obj._update_keys()
|
708
|
+
return new_obj
|
709
|
+
|
710
|
+
def to_json(self, run_or_artifact):
|
711
|
+
"""Returns the JSON representation expected by the backend.
|
712
|
+
|
713
|
+
<!-- lazydoc-ignore: internal -->
|
714
|
+
"""
|
715
|
+
json_dict = super().to_json(run_or_artifact)
|
716
|
+
|
717
|
+
if self.log_mode == "INCREMENTAL":
|
718
|
+
if self._previous_increments_paths is None:
|
719
|
+
self._previous_increments_paths = []
|
720
|
+
if self._increment_num is None:
|
721
|
+
self._increment_num = 0
|
722
|
+
|
723
|
+
json_dict.update(
|
724
|
+
{
|
725
|
+
"increment_num": self._increment_num,
|
726
|
+
"previous_increments_paths": self._previous_increments_paths,
|
727
|
+
}
|
728
|
+
)
|
729
|
+
|
730
|
+
if isinstance(run_or_artifact, wandb.Run):
|
731
|
+
if self.log_mode == "INCREMENTAL":
|
732
|
+
wbvalue_type = "incremental-table-file"
|
733
|
+
else:
|
734
|
+
wbvalue_type = "table-file"
|
735
|
+
|
736
|
+
json_dict.update(
|
737
|
+
{
|
738
|
+
"_type": wbvalue_type,
|
739
|
+
"ncols": len(self.columns),
|
740
|
+
"nrows": len(self.data),
|
741
|
+
"log_mode": self.log_mode,
|
742
|
+
}
|
743
|
+
)
|
744
|
+
|
745
|
+
elif isinstance(run_or_artifact, wandb.Artifact):
|
746
|
+
artifact = run_or_artifact
|
747
|
+
mapped_data = []
|
748
|
+
data = self._to_table_json(Table.MAX_ARTIFACT_ROWS)["data"]
|
749
|
+
|
750
|
+
ndarray_col_ndxs = set()
|
751
|
+
for col_ndx, col_name in enumerate(self.columns):
|
752
|
+
col_type = self._column_types.params["type_map"][col_name]
|
753
|
+
ndarray_type = None
|
754
|
+
if isinstance(col_type, _dtypes.NDArrayType):
|
755
|
+
ndarray_type = col_type
|
756
|
+
elif isinstance(col_type, _dtypes.UnionType):
|
757
|
+
for t in col_type.params["allowed_types"]:
|
758
|
+
if isinstance(t, _dtypes.NDArrayType):
|
759
|
+
ndarray_type = t
|
760
|
+
|
761
|
+
# Do not serialize 1d arrays - these are likely embeddings and
|
762
|
+
# will not have the same cost as higher dimensional arrays
|
763
|
+
is_1d_array = (
|
764
|
+
ndarray_type is not None
|
765
|
+
and "shape" in ndarray_type._params
|
766
|
+
and isinstance(ndarray_type._params["shape"], list)
|
767
|
+
and len(ndarray_type._params["shape"]) == 1
|
768
|
+
and ndarray_type._params["shape"][0]
|
769
|
+
<= self._MAX_EMBEDDING_DIMENSIONS
|
770
|
+
)
|
771
|
+
if is_1d_array:
|
772
|
+
self._column_types.params["type_map"][col_name] = _dtypes.ListType(
|
773
|
+
_dtypes.NumberType, ndarray_type._params["shape"][0]
|
774
|
+
)
|
775
|
+
elif ndarray_type is not None:
|
776
|
+
np = util.get_module(
|
777
|
+
"numpy",
|
778
|
+
required="Serializing NumPy requires NumPy to be installed.",
|
779
|
+
)
|
780
|
+
file_name = f"{str(col_name)}_{runid.generate_id()}.npz"
|
781
|
+
npz_file_name = os.path.join(MEDIA_TMP.name, file_name)
|
782
|
+
np.savez_compressed(
|
783
|
+
npz_file_name,
|
784
|
+
**{
|
785
|
+
str(col_name): self.get_column(col_name, convert_to="numpy")
|
786
|
+
},
|
787
|
+
)
|
788
|
+
entry = artifact.add_file(
|
789
|
+
npz_file_name, "media/serialized_data/" + file_name, is_tmp=True
|
790
|
+
)
|
791
|
+
ndarray_type._set_serialization_path(entry.path, str(col_name))
|
792
|
+
ndarray_col_ndxs.add(col_ndx)
|
793
|
+
|
794
|
+
for row in data:
|
795
|
+
mapped_row = []
|
796
|
+
for ndx, v in enumerate(row):
|
797
|
+
if ndx in ndarray_col_ndxs:
|
798
|
+
mapped_row.append(None)
|
799
|
+
else:
|
800
|
+
mapped_row.append(_json_helper(v, artifact))
|
801
|
+
mapped_data.append(mapped_row)
|
802
|
+
|
803
|
+
json_dict.update(
|
804
|
+
{
|
805
|
+
"_type": Table._log_type,
|
806
|
+
"columns": self.columns,
|
807
|
+
"data": mapped_data,
|
808
|
+
"ncols": len(self.columns),
|
809
|
+
"nrows": len(mapped_data),
|
810
|
+
"column_types": self._column_types.to_json(artifact),
|
811
|
+
"log_mode": self.log_mode,
|
812
|
+
}
|
813
|
+
)
|
814
|
+
else:
|
815
|
+
raise TypeError("to_json accepts wandb_run.Run or wandb_artifact.Artifact")
|
816
|
+
|
817
|
+
return json_dict
|
818
|
+
|
819
|
+
def iterrows(self):
|
820
|
+
"""Returns the table data by row, showing the index of the row and the relevant data.
|
821
|
+
|
822
|
+
Yields:
|
823
|
+
------
|
824
|
+
index: The index of the row. Using this value in other W&B tables
|
825
|
+
will automatically build a relationship between the tables
|
826
|
+
row: The data of the row.
|
827
|
+
|
828
|
+
<!-- lazydoc-ignore: internal -->
|
829
|
+
"""
|
830
|
+
for ndx in range(len(self.data)):
|
831
|
+
index = _TableIndex(ndx)
|
832
|
+
index.set_table(self)
|
833
|
+
yield index, self.data[ndx]
|
834
|
+
|
835
|
+
@allow_relogging_after_mutation
|
836
|
+
def set_pk(self, col_name):
|
837
|
+
"""Set primary key type for Table object.
|
838
|
+
|
839
|
+
<!-- lazydoc-ignore: internal -->
|
840
|
+
"""
|
841
|
+
# TODO: Docs
|
842
|
+
assert col_name in self.columns
|
843
|
+
self.cast(col_name, _PrimaryKeyType())
|
844
|
+
|
845
|
+
@allow_relogging_after_mutation
|
846
|
+
def set_fk(self, col_name, table, table_col):
|
847
|
+
"""Set foreign key type for Table object.
|
848
|
+
|
849
|
+
<!-- lazydoc-ignore: internal -->
|
850
|
+
"""
|
851
|
+
# TODO: Docs
|
852
|
+
assert col_name in self.columns
|
853
|
+
assert col_name != self._pk_col
|
854
|
+
self.cast(col_name, _ForeignKeyType(table, table_col))
|
855
|
+
|
856
|
+
def _update_keys(self, force_last=False):
|
857
|
+
"""Updates the known key-like columns based on current column types.
|
858
|
+
|
859
|
+
If the state has been updated since the last update, wraps the data
|
860
|
+
appropriately in the Key classes.
|
861
|
+
|
862
|
+
Args:
|
863
|
+
force_last: (bool) Wraps the last column of data even if there
|
864
|
+
are no key updates.
|
865
|
+
"""
|
866
|
+
_pk_col = None
|
867
|
+
_fk_cols = set()
|
868
|
+
|
869
|
+
# Buildup the known keys from column types
|
870
|
+
c_types = self._column_types.params["type_map"]
|
871
|
+
for t in c_types:
|
872
|
+
if isinstance(c_types[t], _PrimaryKeyType):
|
873
|
+
_pk_col = t
|
874
|
+
elif isinstance(c_types[t], _ForeignKeyType) or isinstance(
|
875
|
+
c_types[t], _ForeignIndexType
|
876
|
+
):
|
877
|
+
_fk_cols.add(t)
|
878
|
+
|
879
|
+
# If there are updates to perform, safely update them
|
880
|
+
has_update = _pk_col != self._pk_col or _fk_cols != self._fk_cols
|
881
|
+
if has_update:
|
882
|
+
# If we removed the PK
|
883
|
+
if _pk_col is None and self._pk_col is not None:
|
884
|
+
raise AssertionError(
|
885
|
+
f"Cannot unset primary key (column {self._pk_col})"
|
886
|
+
)
|
887
|
+
# If there is a removed FK
|
888
|
+
if len(self._fk_cols - _fk_cols) > 0:
|
889
|
+
raise AssertionError(
|
890
|
+
f"Cannot unset foreign key. Attempted to unset ({self._fk_cols - _fk_cols})"
|
891
|
+
)
|
892
|
+
|
893
|
+
self._pk_col = _pk_col
|
894
|
+
self._fk_cols = _fk_cols
|
895
|
+
|
896
|
+
# Apply updates to data only if there are update or the caller
|
897
|
+
# requested the final row to be updated
|
898
|
+
if has_update or force_last:
|
899
|
+
self._apply_key_updates(not has_update)
|
900
|
+
|
901
|
+
def _apply_key_updates(self, only_last=False):
|
902
|
+
"""Appropriately wraps the underlying data in special Key classes.
|
903
|
+
|
904
|
+
Args:
|
905
|
+
only_last: only apply the updates to the last row (used for performance when
|
906
|
+
the caller knows that the only new data is the last row and no updates were
|
907
|
+
applied to the column types)
|
908
|
+
"""
|
909
|
+
c_types = self._column_types.params["type_map"]
|
910
|
+
|
911
|
+
# Define a helper function which will wrap the data of a single row
|
912
|
+
# in the appropriate class wrapper.
|
913
|
+
def update_row(row_ndx):
|
914
|
+
for fk_col in self._fk_cols:
|
915
|
+
col_ndx = self.columns.index(fk_col)
|
916
|
+
|
917
|
+
# Wrap the Foreign Keys
|
918
|
+
if isinstance(c_types[fk_col], _ForeignKeyType) and not isinstance(
|
919
|
+
self.data[row_ndx][col_ndx], _TableKey
|
920
|
+
):
|
921
|
+
self.data[row_ndx][col_ndx] = _TableKey(self.data[row_ndx][col_ndx])
|
922
|
+
self.data[row_ndx][col_ndx].set_table(
|
923
|
+
c_types[fk_col].params["table"],
|
924
|
+
c_types[fk_col].params["col_name"],
|
925
|
+
)
|
926
|
+
|
927
|
+
# Wrap the Foreign Indexes
|
928
|
+
elif isinstance(c_types[fk_col], _ForeignIndexType) and not isinstance(
|
929
|
+
self.data[row_ndx][col_ndx], _TableIndex
|
930
|
+
):
|
931
|
+
self.data[row_ndx][col_ndx] = _TableIndex(
|
932
|
+
self.data[row_ndx][col_ndx]
|
933
|
+
)
|
934
|
+
self.data[row_ndx][col_ndx].set_table(
|
935
|
+
c_types[fk_col].params["table"]
|
936
|
+
)
|
937
|
+
|
938
|
+
# Wrap the Primary Key
|
939
|
+
if self._pk_col is not None:
|
940
|
+
col_ndx = self.columns.index(self._pk_col)
|
941
|
+
self.data[row_ndx][col_ndx] = _TableKey(self.data[row_ndx][col_ndx])
|
942
|
+
self.data[row_ndx][col_ndx].set_table(self, self._pk_col)
|
943
|
+
|
944
|
+
if only_last:
|
945
|
+
update_row(len(self.data) - 1)
|
946
|
+
else:
|
947
|
+
for row_ndx in range(len(self.data)):
|
948
|
+
update_row(row_ndx)
|
949
|
+
|
950
|
+
@ensure_not_incremental
|
951
|
+
@allow_relogging_after_mutation
|
952
|
+
def add_column(self, name, data, optional=False):
|
953
|
+
"""Adds a column of data to the table.
|
954
|
+
|
955
|
+
Args:
|
956
|
+
name: (str) - the unique name of the column
|
957
|
+
data: (list | np.array) - a column of homogeneous data
|
958
|
+
optional: (bool) - if null-like values are permitted
|
959
|
+
"""
|
960
|
+
assert isinstance(name, str) and name not in self.columns
|
961
|
+
is_np = util.is_numpy_array(data)
|
962
|
+
assert isinstance(data, list) or is_np
|
963
|
+
assert isinstance(optional, bool)
|
964
|
+
is_first_col = len(self.columns) == 0
|
965
|
+
assert is_first_col or len(data) == len(self.data), (
|
966
|
+
f"Expected length {len(self.data)}, found {len(data)}"
|
967
|
+
)
|
968
|
+
|
969
|
+
# Add the new data
|
970
|
+
for ndx in range(max(len(data), len(self.data))):
|
971
|
+
if is_first_col:
|
972
|
+
self.data.append([])
|
973
|
+
if is_np:
|
974
|
+
self.data[ndx].append(data[ndx])
|
975
|
+
else:
|
976
|
+
self.data[ndx].append(data[ndx])
|
977
|
+
# add the column
|
978
|
+
self.columns.append(name)
|
979
|
+
|
980
|
+
try:
|
981
|
+
self.cast(name, _dtypes.UnknownType(), optional=optional)
|
982
|
+
except TypeError:
|
983
|
+
# Undo the changes
|
984
|
+
if is_first_col:
|
985
|
+
self.data = []
|
986
|
+
self.columns = []
|
987
|
+
else:
|
988
|
+
for ndx in range(len(self.data)):
|
989
|
+
self.data[ndx] = self.data[ndx][:-1]
|
990
|
+
self.columns = self.columns[:-1]
|
991
|
+
raise
|
992
|
+
|
993
|
+
def get_column(self, name, convert_to=None):
|
994
|
+
"""Retrieves a column from the table and optionally converts it to a NumPy object.
|
995
|
+
|
996
|
+
Args:
|
997
|
+
name: (str) - the name of the column
|
998
|
+
convert_to: (str, optional)
|
999
|
+
- "numpy": will convert the underlying data to numpy object
|
1000
|
+
"""
|
1001
|
+
assert name in self.columns
|
1002
|
+
assert convert_to is None or convert_to == "numpy"
|
1003
|
+
if convert_to == "numpy":
|
1004
|
+
np = util.get_module(
|
1005
|
+
"numpy", required="Converting to NumPy requires installing NumPy"
|
1006
|
+
)
|
1007
|
+
col = []
|
1008
|
+
col_ndx = self.columns.index(name)
|
1009
|
+
for row in self.data:
|
1010
|
+
item = row[col_ndx]
|
1011
|
+
if convert_to is not None and isinstance(item, WBValue):
|
1012
|
+
item = item.to_data_array()
|
1013
|
+
col.append(item)
|
1014
|
+
if convert_to == "numpy":
|
1015
|
+
col = np.array(col)
|
1016
|
+
return col
|
1017
|
+
|
1018
|
+
def get_index(self):
|
1019
|
+
"""Returns an array of row indexes for use in other tables to create links."""
|
1020
|
+
ndxs = []
|
1021
|
+
for ndx in range(len(self.data)):
|
1022
|
+
index = _TableIndex(ndx)
|
1023
|
+
index.set_table(self)
|
1024
|
+
ndxs.append(index)
|
1025
|
+
return ndxs
|
1026
|
+
|
1027
|
+
def get_dataframe(self):
|
1028
|
+
"""Returns a `pandas.DataFrame` of the table."""
|
1029
|
+
pd = util.get_module(
|
1030
|
+
"pandas",
|
1031
|
+
required="Converting to pandas.DataFrame requires installing pandas",
|
1032
|
+
)
|
1033
|
+
return pd.DataFrame.from_records(self.data, columns=self.columns)
|
1034
|
+
|
1035
|
+
def index_ref(self, index):
|
1036
|
+
"""Gets a reference of the index of a row in the table.
|
1037
|
+
|
1038
|
+
<!-- lazydoc-ignore: internal -->
|
1039
|
+
"""
|
1040
|
+
assert index < len(self.data)
|
1041
|
+
_index = _TableIndex(index)
|
1042
|
+
_index.set_table(self)
|
1043
|
+
return _index
|
1044
|
+
|
1045
|
+
@ensure_not_incremental
|
1046
|
+
@allow_relogging_after_mutation
|
1047
|
+
def add_computed_columns(self, fn):
|
1048
|
+
"""Adds one or more computed columns based on existing data.
|
1049
|
+
|
1050
|
+
Args:
|
1051
|
+
fn: A function which accepts one or two parameters, ndx (int) and
|
1052
|
+
row (dict), which is expected to return a dict representing
|
1053
|
+
new columns for that row, keyed by the new column names.
|
1054
|
+
- `ndx` is an integer representing the index of the row. Only included if `include_ndx`
|
1055
|
+
is set to `True`.
|
1056
|
+
- `row` is a dictionary keyed by existing columns
|
1057
|
+
"""
|
1058
|
+
new_columns = {}
|
1059
|
+
for ndx, row in self.iterrows():
|
1060
|
+
row_dict = {self.columns[i]: row[i] for i in range(len(self.columns))}
|
1061
|
+
new_row_dict = fn(ndx, row_dict)
|
1062
|
+
assert isinstance(new_row_dict, dict)
|
1063
|
+
for key in new_row_dict:
|
1064
|
+
new_columns[key] = new_columns.get(key, [])
|
1065
|
+
new_columns[key].append(new_row_dict[key])
|
1066
|
+
for new_col_name in new_columns:
|
1067
|
+
self.add_column(new_col_name, new_columns[new_col_name])
|
1068
|
+
|
1069
|
+
|
1070
|
+
class _PartitionTablePartEntry:
|
1071
|
+
"""Helper class for PartitionTable to track its parts."""
|
1072
|
+
|
1073
|
+
def __init__(self, entry, source_artifact):
|
1074
|
+
self.entry = entry
|
1075
|
+
self.source_artifact = source_artifact
|
1076
|
+
self._part = None
|
1077
|
+
|
1078
|
+
def get_part(self):
|
1079
|
+
if self._part is None:
|
1080
|
+
self._part = self.source_artifact.get(self.entry.path)
|
1081
|
+
return self._part
|
1082
|
+
|
1083
|
+
def free(self):
|
1084
|
+
self._part = None
|
1085
|
+
|
1086
|
+
|
1087
|
+
class PartitionedTable(Media):
|
1088
|
+
"""A table which is composed of multiple sub-tables.
|
1089
|
+
|
1090
|
+
Currently, PartitionedTable is designed to point to a directory within an
|
1091
|
+
artifact.
|
1092
|
+
"""
|
1093
|
+
|
1094
|
+
_log_type = "partitioned-table"
|
1095
|
+
|
1096
|
+
def __init__(self, parts_path):
|
1097
|
+
"""Initialize a PartitionedTable.
|
1098
|
+
|
1099
|
+
Args:
|
1100
|
+
parts_path (str): path to a directory of tables in the artifact.
|
1101
|
+
"""
|
1102
|
+
super().__init__()
|
1103
|
+
self.parts_path = parts_path
|
1104
|
+
self._loaded_part_entries = {}
|
1105
|
+
|
1106
|
+
def to_json(self, artifact_or_run):
|
1107
|
+
json_obj = {
|
1108
|
+
"_type": PartitionedTable._log_type,
|
1109
|
+
}
|
1110
|
+
if isinstance(artifact_or_run, wandb.Run):
|
1111
|
+
artifact_entry_url = self._get_artifact_entry_ref_url()
|
1112
|
+
if artifact_entry_url is None:
|
1113
|
+
raise ValueError(
|
1114
|
+
"PartitionedTables must first be added to an Artifact before logging to a Run"
|
1115
|
+
)
|
1116
|
+
json_obj["artifact_path"] = artifact_entry_url
|
1117
|
+
else:
|
1118
|
+
json_obj["parts_path"] = self.parts_path
|
1119
|
+
return json_obj
|
1120
|
+
|
1121
|
+
@classmethod
|
1122
|
+
def from_json(cls, json_obj, source_artifact):
|
1123
|
+
instance = cls(json_obj["parts_path"])
|
1124
|
+
entries = source_artifact.manifest.get_entries_in_directory(
|
1125
|
+
json_obj["parts_path"]
|
1126
|
+
)
|
1127
|
+
for entry in entries:
|
1128
|
+
instance._add_part_entry(entry, source_artifact)
|
1129
|
+
return instance
|
1130
|
+
|
1131
|
+
def iterrows(self):
|
1132
|
+
"""Iterate over rows as (ndx, row).
|
1133
|
+
|
1134
|
+
Args:
|
1135
|
+
index (int): The index of the row.
|
1136
|
+
row (List[any]): The data of the row.
|
1137
|
+
"""
|
1138
|
+
columns = None
|
1139
|
+
ndx = 0
|
1140
|
+
for entry_path in self._loaded_part_entries:
|
1141
|
+
part = self._loaded_part_entries[entry_path].get_part()
|
1142
|
+
if columns is None:
|
1143
|
+
columns = part.columns
|
1144
|
+
elif columns != part.columns:
|
1145
|
+
raise ValueError(
|
1146
|
+
f"Table parts have non-matching columns. {columns} != {part.columns}"
|
1147
|
+
)
|
1148
|
+
for _, row in part.iterrows():
|
1149
|
+
yield ndx, row
|
1150
|
+
ndx += 1
|
1151
|
+
|
1152
|
+
self._loaded_part_entries[entry_path].free()
|
1153
|
+
|
1154
|
+
def _add_part_entry(self, entry, source_artifact):
|
1155
|
+
self._loaded_part_entries[entry.path] = _PartitionTablePartEntry(
|
1156
|
+
entry, source_artifact
|
1157
|
+
)
|
1158
|
+
|
1159
|
+
def __ne__(self, other):
|
1160
|
+
return not self.__eq__(other)
|
1161
|
+
|
1162
|
+
def __eq__(self, other):
|
1163
|
+
return isinstance(other, self.__class__) and self.parts_path == other.parts_path
|
1164
|
+
|
1165
|
+
def bind_to_run(self, *args, **kwargs):
|
1166
|
+
raise ValueError("PartitionedTables cannot be bound to runs")
|
1167
|
+
|
1168
|
+
|
1169
|
+
class JoinedTable(Media):
|
1170
|
+
"""Join two tables for visualization in the Artifact UI.
|
1171
|
+
|
1172
|
+
Args:
|
1173
|
+
table1 (str, wandb.Table, ArtifactManifestEntry):
|
1174
|
+
the path to a wandb.Table in an artifact, the table object, or ArtifactManifestEntry
|
1175
|
+
table2 (str, wandb.Table):
|
1176
|
+
the path to a wandb.Table in an artifact, the table object, or ArtifactManifestEntry
|
1177
|
+
join_key (str, [str, str]):
|
1178
|
+
key or keys to perform the join
|
1179
|
+
"""
|
1180
|
+
|
1181
|
+
_log_type = "joined-table"
|
1182
|
+
|
1183
|
+
def __init__(self, table1, table2, join_key):
|
1184
|
+
super().__init__()
|
1185
|
+
|
1186
|
+
if not isinstance(join_key, str) and (
|
1187
|
+
not isinstance(join_key, list) or len(join_key) != 2
|
1188
|
+
):
|
1189
|
+
raise ValueError(
|
1190
|
+
"JoinedTable join_key should be a string or a list of two strings"
|
1191
|
+
)
|
1192
|
+
|
1193
|
+
if not self._validate_table_input(table1):
|
1194
|
+
raise ValueError(
|
1195
|
+
"JoinedTable table1 should be an artifact path to a table or wandb.Table object"
|
1196
|
+
)
|
1197
|
+
|
1198
|
+
if not self._validate_table_input(table2):
|
1199
|
+
raise ValueError(
|
1200
|
+
"JoinedTable table2 should be an artifact path to a table or wandb.Table object"
|
1201
|
+
)
|
1202
|
+
|
1203
|
+
self._table1 = table1
|
1204
|
+
self._table2 = table2
|
1205
|
+
self._join_key = join_key
|
1206
|
+
|
1207
|
+
@classmethod
|
1208
|
+
def from_json(cls, json_obj, source_artifact):
|
1209
|
+
t1 = source_artifact.get(json_obj["table1"])
|
1210
|
+
if t1 is None:
|
1211
|
+
t1 = json_obj["table1"]
|
1212
|
+
|
1213
|
+
t2 = source_artifact.get(json_obj["table2"])
|
1214
|
+
if t2 is None:
|
1215
|
+
t2 = json_obj["table2"]
|
1216
|
+
|
1217
|
+
return cls(
|
1218
|
+
t1,
|
1219
|
+
t2,
|
1220
|
+
json_obj["join_key"],
|
1221
|
+
)
|
1222
|
+
|
1223
|
+
@staticmethod
|
1224
|
+
def _validate_table_input(table):
|
1225
|
+
"""Helper method to validate that the table input is one of the 3 supported types."""
|
1226
|
+
return (
|
1227
|
+
(isinstance(table, str) and table.endswith(".table.json"))
|
1228
|
+
or isinstance(table, Table)
|
1229
|
+
or isinstance(table, PartitionedTable)
|
1230
|
+
or (hasattr(table, "ref_url") and table.ref_url().endswith(".table.json"))
|
1231
|
+
)
|
1232
|
+
|
1233
|
+
def _ensure_table_in_artifact(self, table, artifact, table_ndx):
|
1234
|
+
"""Helper method to add the table to the incoming artifact. Returns the path."""
|
1235
|
+
if isinstance(table, Table) or isinstance(table, PartitionedTable):
|
1236
|
+
table_name = f"t{table_ndx}_{str(id(self))}"
|
1237
|
+
if (
|
1238
|
+
table._artifact_source is not None
|
1239
|
+
and table._artifact_source.name is not None
|
1240
|
+
):
|
1241
|
+
table_name = os.path.basename(table._artifact_source.name)
|
1242
|
+
entry = artifact.add(table, table_name)
|
1243
|
+
table = entry.path
|
1244
|
+
# Check if this is an ArtifactManifestEntry
|
1245
|
+
elif hasattr(table, "ref_url"):
|
1246
|
+
# Give the new object a unique, yet deterministic name
|
1247
|
+
name = binascii.hexlify(base64.standard_b64decode(table.digest)).decode(
|
1248
|
+
"ascii"
|
1249
|
+
)[:20]
|
1250
|
+
entry = artifact.add_reference(
|
1251
|
+
table.ref_url(), "{}.{}.json".format(name, table.name.split(".")[-2])
|
1252
|
+
)[0]
|
1253
|
+
table = entry.path
|
1254
|
+
|
1255
|
+
err_str = "JoinedTable table:{} not found in artifact. Add a table to the artifact using Artifact#add(<table>, {}) before adding this JoinedTable"
|
1256
|
+
if table not in artifact._manifest.entries:
|
1257
|
+
raise ValueError(err_str.format(table, table))
|
1258
|
+
|
1259
|
+
return table
|
1260
|
+
|
1261
|
+
def to_json(self, artifact_or_run):
|
1262
|
+
json_obj = {
|
1263
|
+
"_type": JoinedTable._log_type,
|
1264
|
+
}
|
1265
|
+
if isinstance(artifact_or_run, wandb.Run):
|
1266
|
+
artifact_entry_url = self._get_artifact_entry_ref_url()
|
1267
|
+
if artifact_entry_url is None:
|
1268
|
+
raise ValueError(
|
1269
|
+
"JoinedTables must first be added to an Artifact before logging to a Run"
|
1270
|
+
)
|
1271
|
+
json_obj["artifact_path"] = artifact_entry_url
|
1272
|
+
else:
|
1273
|
+
table1 = self._ensure_table_in_artifact(self._table1, artifact_or_run, 1)
|
1274
|
+
table2 = self._ensure_table_in_artifact(self._table2, artifact_or_run, 2)
|
1275
|
+
json_obj.update(
|
1276
|
+
{
|
1277
|
+
"table1": table1,
|
1278
|
+
"table2": table2,
|
1279
|
+
"join_key": self._join_key,
|
1280
|
+
}
|
1281
|
+
)
|
1282
|
+
return json_obj
|
1283
|
+
|
1284
|
+
def __ne__(self, other):
|
1285
|
+
return not self.__eq__(other)
|
1286
|
+
|
1287
|
+
def _eq_debug(self, other, should_assert=False):
|
1288
|
+
eq = isinstance(other, JoinedTable)
|
1289
|
+
assert not should_assert or eq, (
|
1290
|
+
f"Found type {other.__class__}, expected {JoinedTable}"
|
1291
|
+
)
|
1292
|
+
eq = eq and self._join_key == other._join_key
|
1293
|
+
assert not should_assert or eq, (
|
1294
|
+
f"Found {other._join_key} join key, expected {self._join_key}"
|
1295
|
+
)
|
1296
|
+
eq = eq and self._table1._eq_debug(other._table1, should_assert)
|
1297
|
+
eq = eq and self._table2._eq_debug(other._table2, should_assert)
|
1298
|
+
return eq
|
1299
|
+
|
1300
|
+
def __eq__(self, other):
|
1301
|
+
return self._eq_debug(other, False)
|
1302
|
+
|
1303
|
+
def bind_to_run(self, *args, **kwargs):
|
1304
|
+
raise ValueError("JoinedTables cannot be bound to runs")
|
1305
|
+
|
1306
|
+
|
1307
|
+
class _TableType(_dtypes.Type):
|
1308
|
+
name = "table"
|
1309
|
+
legacy_names = ["wandb.Table"]
|
1310
|
+
types = [Table]
|
1311
|
+
|
1312
|
+
def __init__(self, column_types=None):
|
1313
|
+
if column_types is None:
|
1314
|
+
column_types = _dtypes.UnknownType()
|
1315
|
+
if isinstance(column_types, dict):
|
1316
|
+
column_types = _dtypes.TypedDictType(column_types)
|
1317
|
+
elif not (
|
1318
|
+
isinstance(column_types, _dtypes.TypedDictType)
|
1319
|
+
or isinstance(column_types, _dtypes.UnknownType)
|
1320
|
+
):
|
1321
|
+
raise TypeError("column_types must be a dict or TypedDictType")
|
1322
|
+
|
1323
|
+
self.params.update({"column_types": column_types})
|
1324
|
+
|
1325
|
+
def assign_type(self, wb_type=None):
|
1326
|
+
if isinstance(wb_type, _TableType):
|
1327
|
+
column_types = self.params["column_types"].assign_type(
|
1328
|
+
wb_type.params["column_types"]
|
1329
|
+
)
|
1330
|
+
if not isinstance(column_types, _dtypes.InvalidType):
|
1331
|
+
return _TableType(column_types)
|
1332
|
+
|
1333
|
+
return _dtypes.InvalidType()
|
1334
|
+
|
1335
|
+
@classmethod
|
1336
|
+
def from_obj(cls, py_obj):
|
1337
|
+
if not isinstance(py_obj, Table):
|
1338
|
+
raise TypeError("py_obj must be a wandb.Table")
|
1339
|
+
else:
|
1340
|
+
return cls(py_obj._column_types)
|
1341
|
+
|
1342
|
+
|
1343
|
+
class _JoinedTableType(_dtypes.Type):
|
1344
|
+
name = "joined-table"
|
1345
|
+
types = [JoinedTable]
|
1346
|
+
|
1347
|
+
|
1348
|
+
class _PartitionedTableType(_dtypes.Type):
|
1349
|
+
name = "partitioned-table"
|
1350
|
+
types = [PartitionedTable]
|
1351
|
+
|
1352
|
+
|
1353
|
+
_dtypes.TypeRegistry.add(_TableType)
|
1354
|
+
_dtypes.TypeRegistry.add(_JoinedTableType)
|
1355
|
+
_dtypes.TypeRegistry.add(_PartitionedTableType)
|
1356
|
+
_dtypes.TypeRegistry.add(_ForeignKeyType)
|
1357
|
+
_dtypes.TypeRegistry.add(_PrimaryKeyType)
|
1358
|
+
_dtypes.TypeRegistry.add(_ForeignIndexType)
|
1359
|
+
|
1360
|
+
|
1361
|
+
def _get_data_from_increments(
|
1362
|
+
json_obj: Dict[str, Any], source_artifact: "artifact.Artifact"
|
1363
|
+
) -> List[Any]:
|
1364
|
+
"""Get data from incremental table artifacts.
|
1365
|
+
|
1366
|
+
Args:
|
1367
|
+
json_obj: The JSON object containing table metadata.
|
1368
|
+
source_artifact: The source artifact containing the table data.
|
1369
|
+
|
1370
|
+
Returns:
|
1371
|
+
List of table rows from all increments.
|
1372
|
+
"""
|
1373
|
+
if "latest" not in source_artifact.aliases:
|
1374
|
+
wandb.termwarn(
|
1375
|
+
(
|
1376
|
+
"It is recommended to use the latest version of the "
|
1377
|
+
"incremental table artifact for ordering guarantees."
|
1378
|
+
),
|
1379
|
+
repeat=False,
|
1380
|
+
)
|
1381
|
+
data: List[Any] = []
|
1382
|
+
increment_num = json_obj.get("increment_num", None)
|
1383
|
+
if increment_num is None:
|
1384
|
+
return data
|
1385
|
+
|
1386
|
+
# Sort by increment number first, then by timestamp if present
|
1387
|
+
# Format of name is: "{incr_num}-{timestamp_ms}.{key}.table.json"
|
1388
|
+
def get_sort_key(key: str) -> Tuple[int, int]:
|
1389
|
+
try:
|
1390
|
+
parts = key.split(".")
|
1391
|
+
increment_parts = parts[0].split("-")
|
1392
|
+
increment_num = int(increment_parts[0])
|
1393
|
+
# If there's a timestamp part, use it for secondary sorting
|
1394
|
+
timestamp = int(increment_parts[1]) if len(increment_parts) > 1 else 0
|
1395
|
+
except (ValueError, IndexError):
|
1396
|
+
wandb.termwarn(
|
1397
|
+
(
|
1398
|
+
f"Could not parse artifact entry for increment {key}."
|
1399
|
+
" The entry name does not follow the naming convention"
|
1400
|
+
" <increment_number>-<timestamp>.<key>.table.json"
|
1401
|
+
" The data in the table will be out of order."
|
1402
|
+
),
|
1403
|
+
repeat=False,
|
1404
|
+
)
|
1405
|
+
return (0, 0)
|
1406
|
+
|
1407
|
+
return (increment_num, timestamp)
|
1408
|
+
|
1409
|
+
sorted_increment_keys = []
|
1410
|
+
for entry_key in source_artifact.manifest.entries:
|
1411
|
+
if entry_key.endswith(".table.json"):
|
1412
|
+
sorted_increment_keys.append(entry_key)
|
1413
|
+
|
1414
|
+
sorted_increment_keys.sort(key=get_sort_key)
|
1415
|
+
|
1416
|
+
for entry_key in sorted_increment_keys:
|
1417
|
+
try:
|
1418
|
+
with open(source_artifact.manifest.entries[entry_key].download()) as f:
|
1419
|
+
table_data = json.load(f)
|
1420
|
+
data.extend(table_data["data"])
|
1421
|
+
except (json.JSONDecodeError, KeyError) as e:
|
1422
|
+
raise wandb.Error(f"Invalid table file {entry_key}") from e
|
1423
|
+
return data
|
1424
|
+
|
1425
|
+
|
1426
|
+
def _process_table_row(
|
1427
|
+
row: List[Any],
|
1428
|
+
timestamp_column_indices: Set[_dtypes.TimestampType],
|
1429
|
+
np_deserialized_columns: Dict[int, Any],
|
1430
|
+
source_artifact: "artifact.Artifact",
|
1431
|
+
row_idx: int,
|
1432
|
+
) -> List[Any]:
|
1433
|
+
"""Convert special columns in a table row to Python types.
|
1434
|
+
|
1435
|
+
Processes a single row of table data by converting timestamp values to
|
1436
|
+
datetime objects, replacing np typed cells with numpy array data,
|
1437
|
+
and initializing media objects from their json value.
|
1438
|
+
|
1439
|
+
|
1440
|
+
Args:
|
1441
|
+
row: The row data to process.
|
1442
|
+
timestamp_column_indices: Set of column indices containing timestamps.
|
1443
|
+
np_deserialized_columns: Dictionary mapping column indices to numpy arrays.
|
1444
|
+
source_artifact: The source artifact containing the table data.
|
1445
|
+
row_idx: The index of the current row.
|
1446
|
+
|
1447
|
+
Returns:
|
1448
|
+
Processed row data.
|
1449
|
+
"""
|
1450
|
+
row_data = []
|
1451
|
+
for c_ndx, item in enumerate(row):
|
1452
|
+
cell: Any
|
1453
|
+
if c_ndx in timestamp_column_indices and isinstance(item, (int, float)):
|
1454
|
+
cell = datetime.datetime.fromtimestamp(
|
1455
|
+
item / 1000, tz=datetime.timezone.utc
|
1456
|
+
)
|
1457
|
+
elif c_ndx in np_deserialized_columns:
|
1458
|
+
cell = np_deserialized_columns[c_ndx][row_idx]
|
1459
|
+
elif (
|
1460
|
+
isinstance(item, dict)
|
1461
|
+
and "_type" in item
|
1462
|
+
and (obj := WBValue.init_from_json(item, source_artifact))
|
1463
|
+
):
|
1464
|
+
cell = obj
|
1465
|
+
else:
|
1466
|
+
cell = item
|
1467
|
+
row_data.append(cell)
|
1468
|
+
return row_data
|