wandb 0.17.0rc2__py3-none-macosx_11_0_arm64.whl → 0.17.1__py3-none-macosx_11_0_arm64.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. wandb/__init__.py +1 -2
  2. wandb/apis/importers/internals/internal.py +0 -1
  3. wandb/apis/importers/wandb.py +12 -7
  4. wandb/apis/internal.py +0 -3
  5. wandb/apis/public/api.py +213 -79
  6. wandb/apis/public/artifacts.py +335 -100
  7. wandb/apis/public/files.py +9 -9
  8. wandb/apis/public/jobs.py +16 -4
  9. wandb/apis/public/projects.py +26 -28
  10. wandb/apis/public/query_generator.py +1 -1
  11. wandb/apis/public/runs.py +163 -65
  12. wandb/apis/public/sweeps.py +2 -2
  13. wandb/apis/reports/__init__.py +1 -7
  14. wandb/apis/reports/v1/__init__.py +5 -27
  15. wandb/apis/reports/v2/__init__.py +7 -19
  16. wandb/apis/workspaces/__init__.py +8 -0
  17. wandb/beta/workflows.py +8 -3
  18. wandb/bin/apple_gpu_stats +0 -0
  19. wandb/bin/wandb-core +0 -0
  20. wandb/cli/cli.py +131 -59
  21. wandb/docker/__init__.py +1 -1
  22. wandb/errors/term.py +10 -2
  23. wandb/filesync/step_checksum.py +1 -4
  24. wandb/filesync/step_prepare.py +4 -24
  25. wandb/filesync/step_upload.py +5 -107
  26. wandb/filesync/upload_job.py +0 -76
  27. wandb/integration/gym/__init__.py +35 -15
  28. wandb/integration/openai/fine_tuning.py +21 -3
  29. wandb/integration/prodigy/prodigy.py +1 -1
  30. wandb/jupyter.py +16 -17
  31. wandb/plot/pr_curve.py +2 -1
  32. wandb/plot/roc_curve.py +2 -1
  33. wandb/{plots → plot}/utils.py +13 -25
  34. wandb/proto/v3/wandb_internal_pb2.py +54 -54
  35. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  36. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  37. wandb/proto/v4/wandb_internal_pb2.py +54 -54
  38. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  39. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  40. wandb/proto/v5/wandb_base_pb2.py +30 -0
  41. wandb/proto/v5/wandb_internal_pb2.py +355 -0
  42. wandb/proto/v5/wandb_server_pb2.py +63 -0
  43. wandb/proto/v5/wandb_settings_pb2.py +45 -0
  44. wandb/proto/v5/wandb_telemetry_pb2.py +41 -0
  45. wandb/proto/wandb_base_pb2.py +2 -0
  46. wandb/proto/wandb_deprecated.py +9 -1
  47. wandb/proto/wandb_generate_deprecated.py +34 -0
  48. wandb/proto/{wandb_internal_codegen.py → wandb_generate_proto.py} +1 -35
  49. wandb/proto/wandb_internal_pb2.py +2 -0
  50. wandb/proto/wandb_server_pb2.py +2 -0
  51. wandb/proto/wandb_settings_pb2.py +2 -0
  52. wandb/proto/wandb_telemetry_pb2.py +2 -0
  53. wandb/sdk/artifacts/artifact.py +68 -22
  54. wandb/sdk/artifacts/artifact_manifest.py +1 -1
  55. wandb/sdk/artifacts/artifact_manifest_entry.py +6 -3
  56. wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +1 -1
  57. wandb/sdk/artifacts/artifact_saver.py +1 -10
  58. wandb/sdk/artifacts/storage_handlers/local_file_handler.py +6 -2
  59. wandb/sdk/artifacts/storage_handlers/multi_handler.py +1 -1
  60. wandb/sdk/artifacts/storage_handlers/tracking_handler.py +6 -4
  61. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +2 -42
  62. wandb/sdk/artifacts/storage_policy.py +1 -12
  63. wandb/sdk/data_types/image.py +1 -1
  64. wandb/sdk/data_types/video.py +4 -2
  65. wandb/sdk/interface/interface.py +13 -0
  66. wandb/sdk/interface/interface_shared.py +1 -1
  67. wandb/sdk/internal/file_pusher.py +2 -5
  68. wandb/sdk/internal/file_stream.py +6 -19
  69. wandb/sdk/internal/internal_api.py +148 -136
  70. wandb/sdk/internal/job_builder.py +207 -135
  71. wandb/sdk/internal/progress.py +0 -28
  72. wandb/sdk/internal/sender.py +102 -39
  73. wandb/sdk/internal/settings_static.py +8 -1
  74. wandb/sdk/internal/system/assets/trainium.py +3 -3
  75. wandb/sdk/internal/system/system_info.py +4 -2
  76. wandb/sdk/internal/update.py +1 -1
  77. wandb/sdk/launch/__init__.py +9 -1
  78. wandb/sdk/launch/_launch.py +4 -24
  79. wandb/sdk/launch/_launch_add.py +1 -3
  80. wandb/sdk/launch/_project_spec.py +184 -224
  81. wandb/sdk/launch/agent/agent.py +58 -18
  82. wandb/sdk/launch/agent/config.py +0 -3
  83. wandb/sdk/launch/builder/abstract.py +67 -0
  84. wandb/sdk/launch/builder/build.py +165 -576
  85. wandb/sdk/launch/builder/context_manager.py +235 -0
  86. wandb/sdk/launch/builder/docker_builder.py +7 -23
  87. wandb/sdk/launch/builder/kaniko_builder.py +10 -23
  88. wandb/sdk/launch/builder/templates/dockerfile.py +92 -0
  89. wandb/sdk/launch/create_job.py +51 -45
  90. wandb/sdk/launch/environment/aws_environment.py +26 -1
  91. wandb/sdk/launch/inputs/files.py +148 -0
  92. wandb/sdk/launch/inputs/internal.py +224 -0
  93. wandb/sdk/launch/inputs/manage.py +95 -0
  94. wandb/sdk/launch/runner/abstract.py +2 -2
  95. wandb/sdk/launch/runner/kubernetes_monitor.py +45 -12
  96. wandb/sdk/launch/runner/kubernetes_runner.py +6 -8
  97. wandb/sdk/launch/runner/local_container.py +2 -3
  98. wandb/sdk/launch/runner/local_process.py +8 -29
  99. wandb/sdk/launch/runner/sagemaker_runner.py +20 -14
  100. wandb/sdk/launch/runner/vertex_runner.py +8 -7
  101. wandb/sdk/launch/sweeps/scheduler.py +2 -0
  102. wandb/sdk/launch/sweeps/utils.py +2 -2
  103. wandb/sdk/launch/utils.py +16 -138
  104. wandb/sdk/lib/_settings_toposort_generated.py +2 -5
  105. wandb/sdk/lib/apikey.py +4 -2
  106. wandb/sdk/lib/config_util.py +3 -3
  107. wandb/sdk/lib/proto_util.py +22 -1
  108. wandb/sdk/lib/redirect.py +1 -1
  109. wandb/sdk/service/service.py +2 -1
  110. wandb/sdk/service/streams.py +5 -5
  111. wandb/sdk/wandb_init.py +25 -59
  112. wandb/sdk/wandb_login.py +28 -25
  113. wandb/sdk/wandb_run.py +112 -45
  114. wandb/sdk/wandb_settings.py +33 -64
  115. wandb/sdk/wandb_watch.py +1 -1
  116. wandb/sklearn/plot/classifier.py +4 -6
  117. wandb/sync/sync.py +2 -2
  118. wandb/testing/relay.py +32 -17
  119. wandb/util.py +36 -37
  120. wandb/wandb_agent.py +3 -3
  121. wandb/wandb_controller.py +3 -2
  122. {wandb-0.17.0rc2.dist-info → wandb-0.17.1.dist-info}/METADATA +7 -9
  123. {wandb-0.17.0rc2.dist-info → wandb-0.17.1.dist-info}/RECORD +126 -148
  124. {wandb-0.17.0rc2.dist-info → wandb-0.17.1.dist-info}/WHEEL +1 -1
  125. wandb/apis/reports/v1/_blocks.py +0 -1406
  126. wandb/apis/reports/v1/_helpers.py +0 -70
  127. wandb/apis/reports/v1/_panels.py +0 -1282
  128. wandb/apis/reports/v1/_templates.py +0 -478
  129. wandb/apis/reports/v1/blocks.py +0 -27
  130. wandb/apis/reports/v1/helpers.py +0 -2
  131. wandb/apis/reports/v1/mutations.py +0 -66
  132. wandb/apis/reports/v1/panels.py +0 -17
  133. wandb/apis/reports/v1/report.py +0 -268
  134. wandb/apis/reports/v1/runset.py +0 -144
  135. wandb/apis/reports/v1/templates.py +0 -7
  136. wandb/apis/reports/v1/util.py +0 -406
  137. wandb/apis/reports/v1/validators.py +0 -131
  138. wandb/apis/reports/v2/blocks.py +0 -25
  139. wandb/apis/reports/v2/expr_parsing.py +0 -257
  140. wandb/apis/reports/v2/gql.py +0 -68
  141. wandb/apis/reports/v2/interface.py +0 -1911
  142. wandb/apis/reports/v2/internal.py +0 -867
  143. wandb/apis/reports/v2/metrics.py +0 -6
  144. wandb/apis/reports/v2/panels.py +0 -15
  145. wandb/catboost/__init__.py +0 -9
  146. wandb/fastai/__init__.py +0 -9
  147. wandb/keras/__init__.py +0 -19
  148. wandb/lightgbm/__init__.py +0 -9
  149. wandb/plots/__init__.py +0 -6
  150. wandb/plots/explain_text.py +0 -36
  151. wandb/plots/heatmap.py +0 -81
  152. wandb/plots/named_entity.py +0 -43
  153. wandb/plots/part_of_speech.py +0 -50
  154. wandb/plots/plot_definitions.py +0 -768
  155. wandb/plots/precision_recall.py +0 -121
  156. wandb/plots/roc.py +0 -103
  157. wandb/sacred/__init__.py +0 -3
  158. wandb/xgboost/__init__.py +0 -9
  159. {wandb-0.17.0rc2.dist-info → wandb-0.17.1.dist-info}/entry_points.txt +0 -0
  160. {wandb-0.17.0rc2.dist-info → wandb-0.17.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,235 @@
1
+ import logging
2
+ import os
3
+ import shutil
4
+ import tempfile
5
+ from typing import Tuple
6
+
7
+ from wandb.sdk.launch._project_spec import LaunchProject
8
+ from wandb.sdk.launch.builder.build import image_tag_from_dockerfile_and_source
9
+ from wandb.sdk.launch.errors import LaunchError
10
+ from wandb.sdk.launch.utils import get_current_python_version
11
+
12
+ from .build import (
13
+ _WANDB_DOCKERFILE_NAME,
14
+ get_base_setup,
15
+ get_docker_user,
16
+ get_entrypoint_setup,
17
+ get_requirements_section,
18
+ get_user_setup,
19
+ )
20
+ from .templates.dockerfile import DOCKERFILE_TEMPLATE
21
+
22
+ _logger = logging.getLogger(__name__)
23
+
24
+
25
+ class BuildContextManager:
26
+ """Creates a build context for a container image from job source code.
27
+
28
+ The dockerfile and build context may be specified by the job itself. If not,
29
+ the behavior for creating the build context is as follows:
30
+
31
+ - If a Dockerfile.wandb is found adjacent to the entrypoint, the directory
32
+ containing the entrypoint is used as the build context and Dockerfile.wandb
33
+ is used as the Dockerfile.
34
+
35
+ - If `override_dockerfile` is set on the LaunchProject, the directory
36
+ containing the Dockerfile is used as the build context and the Dockerfile
37
+ is used as the Dockerfile. `override_dockerfile` can be set in a launch
38
+ spec via the `-D` flag to `wandb launch` or in the `overrides` section
39
+ of the launch drawer.
40
+
41
+ - If no dockerfile is set, a Dockerfile is generated from the job's
42
+ requirements and entrypoint.
43
+ """
44
+
45
+ def __init__(self, launch_project: LaunchProject):
46
+ """Initialize a BuildContextManager.
47
+
48
+ Arguments:
49
+ launch_project: The launch project.
50
+ """
51
+ self._launch_project = launch_project
52
+ assert self._launch_project.project_dir is not None
53
+ self._directory = tempfile.mkdtemp()
54
+
55
+ def _generate_dockerfile(self, builder_type: str) -> str:
56
+ """Generate a Dockerfile for the container image.
57
+
58
+ Arguments:
59
+ builder_type: The type of builder to use. One of "docker" or "kaniko".
60
+
61
+ Returns:
62
+ The contents of the Dockerfile.
63
+ """
64
+ launch_project = self._launch_project
65
+ entry_point = (
66
+ launch_project.override_entrypoint or launch_project.get_job_entry_point()
67
+ )
68
+
69
+ # get python versions truncated to major.minor to ensure image availability
70
+ if launch_project.python_version:
71
+ spl = launch_project.python_version.split(".")[:2]
72
+ py_version, py_major = (".".join(spl), spl[0])
73
+ else:
74
+ py_version, py_major = get_current_python_version()
75
+
76
+ python_build_image = (
77
+ f"python:{py_version}" # use full python image for package installation
78
+ )
79
+ requirements_section = get_requirements_section(
80
+ launch_project, self._directory, builder_type
81
+ )
82
+ # ----- stage 2: base -----
83
+ python_base_setup = get_base_setup(launch_project, py_version, py_major)
84
+
85
+ # set up user info
86
+ username, userid = get_docker_user(launch_project, launch_project.resource)
87
+ user_setup = get_user_setup(username, userid, launch_project.resource)
88
+ workdir = f"/home/{username}"
89
+
90
+ assert entry_point is not None
91
+ entrypoint_section = get_entrypoint_setup(entry_point)
92
+
93
+ dockerfile_contents = DOCKERFILE_TEMPLATE.format(
94
+ py_build_image=python_build_image,
95
+ requirements_section=requirements_section,
96
+ base_setup=python_base_setup,
97
+ uid=userid,
98
+ user_setup=user_setup,
99
+ workdir=workdir,
100
+ entrypoint_section=entrypoint_section,
101
+ )
102
+ return dockerfile_contents
103
+
104
+ def create_build_context(self, builder_type: str) -> Tuple[str, str]:
105
+ """Create the build context for the container image.
106
+
107
+ Returns:
108
+ A pair of str: the path to the build context locally and the image
109
+ tag computed from the Dockerfile.
110
+ """
111
+ entrypoint = (
112
+ self._launch_project.get_job_entry_point()
113
+ or self._launch_project.override_entrypoint
114
+ )
115
+ assert entrypoint is not None
116
+ assert entrypoint.name is not None
117
+ assert self._launch_project.project_dir is not None
118
+
119
+ # we use that as the build context.
120
+ build_context_root_dir = self._launch_project.project_dir
121
+ job_build_context = self._launch_project.job_build_context
122
+ if job_build_context:
123
+ full_path = os.path.join(build_context_root_dir, job_build_context)
124
+ if not os.path.exists(full_path):
125
+ raise LaunchError(f"Build context does not exist at {full_path}")
126
+ build_context_root_dir = full_path
127
+
128
+ # This is the case where the user specifies a Dockerfile to use.
129
+ # We use the directory containing the Dockerfile as the build context.
130
+ override_dockerfile = self._launch_project.override_dockerfile
131
+ if override_dockerfile:
132
+ full_path = os.path.join(
133
+ build_context_root_dir,
134
+ override_dockerfile,
135
+ )
136
+ if not os.path.exists(full_path):
137
+ raise LaunchError(f"Dockerfile does not exist at {full_path}")
138
+ shutil.copytree(
139
+ build_context_root_dir,
140
+ self._directory,
141
+ symlinks=True,
142
+ dirs_exist_ok=True,
143
+ ignore=shutil.ignore_patterns("fsmonitor--daemon.ipc"),
144
+ )
145
+ shutil.copy(
146
+ full_path,
147
+ os.path.join(self._directory, _WANDB_DOCKERFILE_NAME),
148
+ )
149
+ return self._directory, image_tag_from_dockerfile_and_source(
150
+ self._launch_project, open(full_path).read()
151
+ )
152
+
153
+ # If the job specifies a Dockerfile, we use that as the Dockerfile.
154
+ job_dockerfile = self._launch_project.job_dockerfile
155
+ if job_dockerfile:
156
+ dockerfile_path = os.path.join(build_context_root_dir, job_dockerfile)
157
+ if not os.path.exists(dockerfile_path):
158
+ raise LaunchError(f"Dockerfile does not exist at {dockerfile_path}")
159
+ shutil.copytree(
160
+ build_context_root_dir,
161
+ self._directory,
162
+ symlinks=True,
163
+ dirs_exist_ok=True,
164
+ ignore=shutil.ignore_patterns("fsmonitor--daemon.ipc"),
165
+ )
166
+ shutil.copy(
167
+ dockerfile_path,
168
+ os.path.join(self._directory, _WANDB_DOCKERFILE_NAME),
169
+ )
170
+ return self._directory, image_tag_from_dockerfile_and_source(
171
+ self._launch_project, open(dockerfile_path).read()
172
+ )
173
+
174
+ # This is the case where we find Dockerfile.wandb adjacent to the
175
+ # entrypoint. We use the entrypoint directory as the build context.
176
+ entrypoint_dir = os.path.dirname(entrypoint.name)
177
+ if entrypoint_dir:
178
+ path = os.path.join(
179
+ build_context_root_dir,
180
+ entrypoint_dir,
181
+ _WANDB_DOCKERFILE_NAME,
182
+ )
183
+ else:
184
+ path = os.path.join(build_context_root_dir, _WANDB_DOCKERFILE_NAME)
185
+ if os.path.exists(
186
+ path
187
+ ): # We found a Dockerfile.wandb adjacent to the entrypoint.
188
+ shutil.copytree(
189
+ os.path.dirname(path),
190
+ self._directory,
191
+ symlinks=True,
192
+ dirs_exist_ok=True,
193
+ ignore=shutil.ignore_patterns("fsmonitor--daemon.ipc"),
194
+ )
195
+ # TODO: remove this once we make things more explicit for users
196
+ if entrypoint_dir:
197
+ new_path = os.path.basename(entrypoint.name)
198
+ entrypoint = self._launch_project.get_job_entry_point()
199
+ if entrypoint is not None:
200
+ entrypoint.update_entrypoint_path(new_path)
201
+ with open(path) as f:
202
+ docker_file_contents = f.read()
203
+ return self._directory, image_tag_from_dockerfile_and_source(
204
+ self._launch_project, docker_file_contents
205
+ )
206
+
207
+ # This is the case where we use our own Dockerfile template. We move
208
+ # the user code into a src directory in the build context.
209
+ dst_path = os.path.join(self._directory, "src")
210
+ assert self._launch_project.project_dir is not None
211
+ shutil.copytree(
212
+ src=self._launch_project.project_dir,
213
+ dst=dst_path,
214
+ symlinks=True,
215
+ ignore=shutil.ignore_patterns("fsmonitor--daemon.ipc"),
216
+ )
217
+ shutil.copy(
218
+ os.path.join(os.path.dirname(__file__), "templates", "_wandb_bootstrap.py"),
219
+ os.path.join(self._directory),
220
+ )
221
+ if self._launch_project.python_version:
222
+ runtime_path = os.path.join(dst_path, "runtime.txt")
223
+ with open(runtime_path, "w") as fp:
224
+ fp.write(f"python-{self._launch_project.python_version}")
225
+
226
+ # TODO: we likely don't need to pass the whole git repo into the container
227
+ # with open(os.path.join(directory, ".dockerignore"), "w") as f:
228
+ # f.write("**/.git")
229
+ with open(os.path.join(self._directory, _WANDB_DOCKERFILE_NAME), "w") as handle:
230
+ docker_file_contents = self._generate_dockerfile(builder_type=builder_type)
231
+ handle.write(docker_file_contents)
232
+ image_tag = image_tag_from_dockerfile_and_source(
233
+ self._launch_project, docker_file_contents
234
+ )
235
+ return self._directory, image_tag
@@ -7,8 +7,7 @@ from typing import Any, Dict, Optional
7
7
  import wandb
8
8
  import wandb.docker as docker
9
9
  from wandb.sdk.launch.agent.job_status_tracker import JobAndRunStatusTracker
10
- from wandb.sdk.launch.builder.abstract import AbstractBuilder
11
- from wandb.sdk.launch.builder.build import registry_from_uri
10
+ from wandb.sdk.launch.builder.abstract import AbstractBuilder, registry_from_uri
12
11
  from wandb.sdk.launch.environment.abstract import AbstractEnvironment
13
12
  from wandb.sdk.launch.registry.abstract import AbstractRegistry
14
13
 
@@ -21,13 +20,8 @@ from ..utils import (
21
20
  event_loop_thread_exec,
22
21
  warn_failed_packages_from_build_logs,
23
22
  )
24
- from .build import (
25
- _WANDB_DOCKERFILE_NAME,
26
- _create_docker_build_ctx,
27
- generate_dockerfile,
28
- image_tag_from_dockerfile_and_source,
29
- validate_docker_installation,
30
- )
23
+ from .build import _WANDB_DOCKERFILE_NAME, validate_docker_installation
24
+ from .context_manager import BuildContextManager
31
25
 
32
26
  _logger = logging.getLogger(__name__)
33
27
 
@@ -41,7 +35,6 @@ class DockerBuilder(AbstractBuilder):
41
35
  """
42
36
 
43
37
  builder_type = "docker"
44
- base_image = "python:3.8"
45
38
  target_platform = "linux/amd64"
46
39
 
47
40
  def __init__(
@@ -124,17 +117,11 @@ class DockerBuilder(AbstractBuilder):
124
117
  await self.verify()
125
118
  await self.login()
126
119
 
127
- dockerfile_str = generate_dockerfile(
128
- launch_project=launch_project,
129
- entry_point=entrypoint,
130
- runner_type=launch_project.resource,
131
- builder_type="docker",
132
- dockerfile=launch_project.override_dockerfile,
133
- )
134
-
135
- image_tag = image_tag_from_dockerfile_and_source(launch_project, dockerfile_str)
136
-
120
+ build_context_manager = BuildContextManager(launch_project=launch_project)
121
+ build_ctx_path, image_tag = build_context_manager.create_build_context("docker")
122
+ dockerfile = os.path.join(build_ctx_path, _WANDB_DOCKERFILE_NAME)
137
123
  repository = None if not self.registry else await self.registry.get_repo_uri()
124
+
138
125
  # if repo is set, use the repo name as the image name
139
126
  if repository:
140
127
  image_uri = f"{repository}:{image_tag}"
@@ -152,9 +139,6 @@ class DockerBuilder(AbstractBuilder):
152
139
  _logger.info(
153
140
  f"image {image_uri} does not already exist in repository, building."
154
141
  )
155
-
156
- build_ctx_path = _create_docker_build_ctx(launch_project, dockerfile_str)
157
- dockerfile = os.path.join(build_ctx_path, _WANDB_DOCKERFILE_NAME)
158
142
  try:
159
143
  output = await event_loop_thread_exec(docker.build)(
160
144
  tags=[image_uri],
@@ -13,8 +13,7 @@ from typing import Any, Dict, Optional
13
13
 
14
14
  import wandb
15
15
  from wandb.sdk.launch.agent.job_status_tracker import JobAndRunStatusTracker
16
- from wandb.sdk.launch.builder.abstract import AbstractBuilder
17
- from wandb.sdk.launch.builder.build import registry_from_uri
16
+ from wandb.sdk.launch.builder.abstract import AbstractBuilder, registry_from_uri
18
17
  from wandb.sdk.launch.environment.abstract import AbstractEnvironment
19
18
  from wandb.sdk.launch.environment.azure_environment import AzureEnvironment
20
19
  from wandb.sdk.launch.registry.abstract import AbstractRegistry
@@ -32,12 +31,8 @@ from ..utils import (
32
31
  get_kube_context_and_api_client,
33
32
  warn_failed_packages_from_build_logs,
34
33
  )
35
- from .build import (
36
- _WANDB_DOCKERFILE_NAME,
37
- _create_docker_build_ctx,
38
- generate_dockerfile,
39
- image_tag_from_dockerfile_and_source,
40
- )
34
+ from .build import _WANDB_DOCKERFILE_NAME
35
+ from .context_manager import BuildContextManager
41
36
 
42
37
  get_module(
43
38
  "kubernetes_asyncio",
@@ -261,17 +256,13 @@ class KanikoBuilder(AbstractBuilder):
261
256
  job_tracker: Optional[JobAndRunStatusTracker] = None,
262
257
  ) -> str:
263
258
  await self.verify()
264
- # kaniko builder doesn't seem to work with a custom user id, need more investigation
265
- dockerfile_str = generate_dockerfile(
266
- launch_project=launch_project,
267
- entry_point=entrypoint,
268
- runner_type=launch_project.resource,
269
- builder_type="kaniko",
270
- dockerfile=launch_project.override_dockerfile,
271
- )
272
- image_tag = image_tag_from_dockerfile_and_source(launch_project, dockerfile_str)
259
+
260
+ build_contex_manager = BuildContextManager(launch_project=launch_project)
261
+ context_path, image_tag = build_contex_manager.create_build_context("kaniko")
262
+ run_id = launch_project.run_id
273
263
  repo_uri = await self.registry.get_repo_uri()
274
264
  image_uri = repo_uri + ":" + image_tag
265
+
275
266
  if (
276
267
  not launch_project.build_required()
277
268
  and await self.registry.check_image_exists(image_uri)
@@ -279,10 +270,6 @@ class KanikoBuilder(AbstractBuilder):
279
270
  return image_uri
280
271
 
281
272
  _logger.info(f"Building image {image_uri}...")
282
-
283
- context_path = _create_docker_build_ctx(launch_project, dockerfile_str)
284
- run_id = launch_project.run_id
285
-
286
273
  _, api_client = await get_kube_context_and_api_client(
287
274
  kubernetes, launch_project.resource_args
288
275
  )
@@ -492,8 +479,8 @@ class KanikoBuilder(AbstractBuilder):
492
479
  }
493
480
  )
494
481
  else:
495
- raise LaunchError(
496
- f"Registry type {type(self.registry)} not supported by kaniko"
482
+ wandb.termwarn(
483
+ f"{LOG_PREFIX}Automatic credential handling is not supported for registry type {type(self.registry)}. Build job: {self.build_job_name}"
497
484
  )
498
485
  volumes.append(
499
486
  {
@@ -0,0 +1,92 @@
1
+ DOCKERFILE_TEMPLATE = """
2
+ # ----- stage 1: build -----
3
+ FROM {py_build_image} as build
4
+
5
+ # requirements section depends on pip vs conda, and presence of buildx
6
+ ENV PIP_PROGRESS_BAR off
7
+ {requirements_section}
8
+
9
+ # ----- stage 2: base -----
10
+ {base_setup}
11
+
12
+ COPY --from=build /env /env
13
+ ENV PATH="/env/bin:$PATH"
14
+
15
+ ENV SHELL /bin/bash
16
+
17
+ # some resources (eg sagemaker) must run on root
18
+ {user_setup}
19
+
20
+ WORKDIR {workdir}
21
+ RUN chown -R {uid} {workdir}
22
+
23
+ # make artifacts cache dir unrelated to build
24
+ RUN mkdir -p {workdir}/.cache && chown -R {uid} {workdir}/.cache
25
+
26
+ # copy code/etc
27
+ COPY --chown={uid} src/ {workdir}
28
+
29
+ ENV PYTHONUNBUFFERED=1
30
+
31
+ {entrypoint_section}
32
+ """
33
+
34
+ # this goes into base_setup in TEMPLATE
35
+ PYTHON_SETUP_TEMPLATE = """
36
+ FROM {py_base_image} as base
37
+ """
38
+
39
+ # this goes into base_setup in TEMPLATE
40
+ ACCELERATOR_SETUP_TEMPLATE = """
41
+ FROM {accelerator_base_image} as base
42
+
43
+ # make non-interactive so build doesn't block on questions
44
+ ENV DEBIAN_FRONTEND=noninteractive
45
+
46
+ # install python
47
+ RUN apt-get update -qq && apt-get install --no-install-recommends -y \
48
+ {python_packages} \
49
+ && apt-get -qq purge && apt-get -qq clean \
50
+ && rm -rf /var/lib/apt/lists/*
51
+
52
+ # make sure `python` points at the right version
53
+ RUN update-alternatives --install /usr/bin/python python /usr/bin/python{py_version} 1 \
54
+ && update-alternatives --install /usr/local/bin/python python /usr/bin/python{py_version} 1
55
+ """
56
+
57
+ # this goes into requirements_section in TEMPLATE
58
+ PIP_TEMPLATE = """
59
+ RUN python -m venv /env
60
+ # make sure we install into the env
61
+ ENV PATH="/env/bin:$PATH"
62
+
63
+ COPY {requirements_files} ./
64
+ {buildx_optional_prefix} {pip_install}
65
+ """
66
+
67
+ # this goes into requirements_section in TEMPLATE
68
+ CONDA_TEMPLATE = """
69
+ COPY src/environment.yml .
70
+ {buildx_optional_prefix} conda env create -f environment.yml -n env
71
+
72
+ # pack the environment so that we can transfer to the base image
73
+ RUN conda install -c conda-forge conda-pack
74
+ RUN conda pack -n env -o /tmp/env.tar && \
75
+ mkdir /env && cd /env && tar xf /tmp/env.tar && \
76
+ rm /tmp/env.tar
77
+ RUN /env/bin/conda-unpack
78
+ """
79
+
80
+ USER_CREATE_TEMPLATE = """
81
+ RUN useradd \
82
+ --create-home \
83
+ --no-log-init \
84
+ --shell /bin/bash \
85
+ --gid 0 \
86
+ --uid {uid} \
87
+ {user} || echo ""
88
+ """
89
+
90
+ ENTRYPOINT_TEMPLATE = """
91
+ ENTRYPOINT {entrypoint}
92
+ """
@@ -10,9 +10,12 @@ import wandb
10
10
  from wandb.apis.internal import Api
11
11
  from wandb.sdk.artifacts.artifact import Artifact
12
12
  from wandb.sdk.internal.job_builder import JobBuilder
13
- from wandb.sdk.launch.builder.build import get_current_python_version
14
13
  from wandb.sdk.launch.git_reference import GitReference
15
- from wandb.sdk.launch.utils import _is_git_uri, get_entrypoint_file
14
+ from wandb.sdk.launch.utils import (
15
+ _is_git_uri,
16
+ get_current_python_version,
17
+ get_entrypoint_file,
18
+ )
16
19
  from wandb.sdk.lib import filesystem
17
20
  from wandb.util import make_artifact_name_safe
18
21
 
@@ -34,6 +37,8 @@ def create_job(
34
37
  runtime: Optional[str] = None,
35
38
  entrypoint: Optional[str] = None,
36
39
  git_hash: Optional[str] = None,
40
+ build_context: Optional[str] = None,
41
+ dockerfile: Optional[str] = None,
37
42
  ) -> Optional[Artifact]:
38
43
  """Create a job from a path, not as the output of a run.
39
44
 
@@ -46,9 +51,12 @@ def create_job(
46
51
  description (Optional[str]): Description of the job.
47
52
  aliases (Optional[List[str]]): Aliases for the job.
48
53
  runtime (Optional[str]): Python runtime of the job, like 3.9.
49
- entrypoint (Optional[str]): Entrypoint of the job.
54
+ entrypoint (Optional[str]): Entrypoint of the job. If build_context is
55
+ provided, path is relative to build_context.
50
56
  git_hash (Optional[str]): Git hash of a specific commit, when using git type jobs.
51
-
57
+ build_context (Optional[str]): Path to the build context, when using image type jobs.
58
+ dockerfile (Optional[str]): Path to the Dockerfile, when using image type jobs.
59
+ If build_context is provided, path is relative to build_context.
52
60
 
53
61
  Returns:
54
62
  Optional[Artifact]: The artifact created by the job, the action (for printing), and job aliases.
@@ -85,6 +93,8 @@ def create_job(
85
93
  runtime,
86
94
  entrypoint,
87
95
  git_hash,
96
+ build_context,
97
+ dockerfile,
88
98
  )
89
99
 
90
100
  return artifact_job
@@ -102,6 +112,8 @@ def _create_job(
102
112
  runtime: Optional[str] = None,
103
113
  entrypoint: Optional[str] = None,
104
114
  git_hash: Optional[str] = None,
115
+ build_context: Optional[str] = None,
116
+ dockerfile: Optional[str] = None,
105
117
  ) -> Tuple[Optional[Artifact], str, List[str]]:
106
118
  wandb.termlog(f"Creating launch job of type: {job_type}...")
107
119
 
@@ -172,7 +184,11 @@ def _create_job(
172
184
  name = job_name
173
185
 
174
186
  # build job artifact, loads wandb-metadata and creates wandb-job.json here
175
- artifact = job_builder.build()
187
+ artifact = job_builder.build(
188
+ api.api,
189
+ dockerfile=dockerfile,
190
+ build_context=build_context,
191
+ )
176
192
  if not artifact:
177
193
  wandb.termerror("JobBuilder failed to build a job")
178
194
  _logger.debug("Failed to build job, check job source and metadata")
@@ -195,7 +211,7 @@ def _create_job(
195
211
  project_name=project,
196
212
  run_name=run.id, # type: ignore # run will be deleted after creation
197
213
  description=description,
198
- metadata=metadata,
214
+ metadata={"_partial": True},
199
215
  is_user_created=True,
200
216
  aliases=[{"artifactCollectionName": name, "alias": a} for a in aliases],
201
217
  )
@@ -229,8 +245,9 @@ def _make_metadata_for_partial_job(
229
245
  entrypoint: Optional[str],
230
246
  ) -> Tuple[Optional[Dict[str, Any]], Optional[List[str]]]:
231
247
  """Create metadata for partial jobs, return metadata and requirements."""
232
- metadata = {"_partial": "v0"}
248
+ metadata = {}
233
249
  if job_type == "git":
250
+ assert entrypoint is not None
234
251
  repo_metadata = _create_repo_metadata(
235
252
  path=path,
236
253
  tempdir=tempdir.name,
@@ -245,12 +262,7 @@ def _make_metadata_for_partial_job(
245
262
  return metadata, None
246
263
 
247
264
  if job_type == "code":
248
- if not entrypoint:
249
- wandb.termerror(
250
- "Artifact jobs must have an entrypoint, either included in the path or specified with -E"
251
- )
252
- return None, None
253
-
265
+ assert entrypoint is not None
254
266
  artifact_metadata, requirements = _create_artifact_metadata(
255
267
  path=path, entrypoint=entrypoint, runtime=runtime
256
268
  )
@@ -276,10 +288,18 @@ def _make_metadata_for_partial_job(
276
288
  return None, None
277
289
 
278
290
 
291
+ def _maybe_warn_python_no_executable(entrypoint: str):
292
+ entrypoint_list = entrypoint.split(" ")
293
+ if len(entrypoint_list) == 1 and entrypoint_list[0].endswith(".py"):
294
+ wandb.termwarn(
295
+ f"Entrypoint {entrypoint} is a python file without an executable, you may want to use `python {entrypoint}` as the entrypoint instead."
296
+ )
297
+
298
+
279
299
  def _create_repo_metadata(
280
300
  path: str,
281
301
  tempdir: str,
282
- entrypoint: Optional[str] = None,
302
+ entrypoint: str,
283
303
  git_hash: Optional[str] = None,
284
304
  runtime: Optional[str] = None,
285
305
  ) -> Optional[Dict[str, Any]]:
@@ -287,6 +307,9 @@ def _create_repo_metadata(
287
307
  if entrypoint and ".." in entrypoint:
288
308
  wandb.termerror("Entrypoint cannot contain backward path traversal")
289
309
  return None
310
+
311
+ _maybe_warn_python_no_executable(entrypoint)
312
+
290
313
  if not _is_git_uri(path):
291
314
  wandb.termerror("Path must be a git URI")
292
315
  return None
@@ -315,32 +338,16 @@ def _create_repo_metadata(
315
338
  with open(os.path.join(local_dir, ".python-version")) as f:
316
339
  python_version = f.read().strip().splitlines()[0]
317
340
  else:
318
- _, python_version = get_current_python_version()
341
+ python_version, _ = get_current_python_version()
319
342
 
320
343
  python_version = _clean_python_version(python_version)
321
344
 
322
- # check if entrypoint is valid
323
- assert entrypoint is not None
324
- entrypoint_list = entrypoint.split(" ")
325
- entrypoint_file = get_entrypoint_file(entrypoint_list)
326
- if not entrypoint_file:
327
- wandb.termerror(
328
- f"Entrypoint {entrypoint} is invalid. An entrypoint should include both an executable and a file, for example 'python train.py'"
329
- )
330
- return None
331
-
332
- if not os.path.exists(os.path.join(local_dir, entrypoint_file)):
333
- wandb.termerror(f"Entrypoint file {entrypoint_file} not found in git repo")
334
- return None
335
-
336
345
  metadata = {
337
346
  "git": {
338
347
  "commit": commit,
339
348
  "remote": ref.url,
340
349
  },
341
- "codePathLocal": entrypoint_file, # not in git context, optionally also set local
342
- "codePath": entrypoint_file,
343
- "entrypoint": entrypoint_list,
350
+ "entrypoint": entrypoint.split(" "),
344
351
  "python": python_version, # used to build container
345
352
  "notebook": False, # partial jobs from notebooks not supported
346
353
  }
@@ -354,13 +361,11 @@ def _create_artifact_metadata(
354
361
  if not os.path.isdir(path):
355
362
  wandb.termerror("Path must be a valid file or directory")
356
363
  return {}, []
364
+
365
+ _maybe_warn_python_no_executable(entrypoint)
366
+
357
367
  entrypoint_list = entrypoint.split(" ")
358
368
  entrypoint_file = get_entrypoint_file(entrypoint_list)
359
- if not entrypoint_file:
360
- wandb.termerror(
361
- f"Entrypoint {entrypoint} is invalid. An entrypoint should include both an executable and a file, for example 'python train.py'"
362
- )
363
- return None, None
364
369
 
365
370
  # read local requirements.txt and dump to temp dir for builder
366
371
  requirements = []
@@ -369,6 +374,9 @@ def _create_artifact_metadata(
369
374
  with open(depspath) as f:
370
375
  requirements = f.read().splitlines()
371
376
 
377
+ if not any(["wandb" in r for r in requirements]):
378
+ wandb.termwarn("wandb is not present in requirements.txt.")
379
+
372
380
  if runtime:
373
381
  python_version = _clean_python_version(runtime)
374
382
  else:
@@ -399,6 +407,7 @@ def _configure_job_builder_for_partial(tmpdir: str, job_source: str) -> JobBuild
399
407
  settings=settings, # type: ignore
400
408
  verbose=True,
401
409
  )
410
+ job_builder._partial = True
402
411
  # never allow notebook runs
403
412
  job_builder._is_notebook_run = False
404
413
  # set run inputs and outputs to empty dicts
@@ -421,15 +430,12 @@ def _make_code_artifact(
421
430
 
422
431
  Returns the name of the eventual job.
423
432
  """
424
- assert entrypoint is not None
425
433
  entrypoint_list = entrypoint.split(" ")
426
- entrypoint_file = get_entrypoint_file(entrypoint_list)
427
- if not entrypoint_file:
428
- wandb.termerror(
429
- f"Entrypoint {entrypoint} is invalid. An entrypoint should include both an executable and a file, for example 'python train.py'"
430
- )
431
- return None
432
-
434
+ # We no longer require the entrypoint to end in an existing file. But we
435
+ # need something to use as the default job artifact name. In the future we
436
+ # may require the user to provide a job name explicitly when calling
437
+ # wandb job create.
438
+ entrypoint_file = entrypoint_list[-1]
433
439
  artifact_name = _make_code_artifact_name(os.path.join(path, entrypoint_file), name)
434
440
  code_artifact = wandb.Artifact(
435
441
  name=artifact_name,