lamindb 1.12.1__py3-none-any.whl → 1.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ from upath import UPath
11
11
 
12
12
  from ..core._mapped_collection import MappedCollection
13
13
  from ..core.storage._backed_access import _open_dataframe
14
- from .artifact import Artifact, _track_run_input
14
+ from .artifact import Artifact, track_run_input
15
15
  from .collection import Collection, _load_concat_artifacts
16
16
 
17
17
  if TYPE_CHECKING:
@@ -55,7 +55,7 @@ class ArtifactSet(Iterable):
55
55
  artifacts: list[Artifact] = list(self)
56
56
  concat_object = _load_concat_artifacts(artifacts, join, **kwargs)
57
57
  # track only if successful
58
- _track_run_input(artifacts, is_run_input)
58
+ track_run_input(artifacts, is_run_input)
59
59
  return concat_object
60
60
 
61
61
  @doc_args(Collection.open.__doc__)
@@ -74,7 +74,7 @@ class ArtifactSet(Iterable):
74
74
 
75
75
  dataframe = _open_dataframe(paths, engine=engine, **kwargs)
76
76
  # track only if successful
77
- _track_run_input(artifacts, is_run_input)
77
+ track_run_input(artifacts, is_run_input)
78
78
  return dataframe
79
79
 
80
80
  @doc_args(Collection.mapped.__doc__)
@@ -122,7 +122,7 @@ class ArtifactSet(Iterable):
122
122
  dtype,
123
123
  )
124
124
  # track only if successful
125
- _track_run_input(artifacts, is_run_input)
125
+ track_run_input(artifacts, is_run_input)
126
126
  return ds
127
127
 
128
128
 
lamindb/models/block.py CHANGED
@@ -71,7 +71,7 @@ class RootBlock(BlockMixin, BaseSQLRecord):
71
71
 
72
72
 
73
73
  class RecordBlock(BlockMixin, BaseSQLRecord):
74
- """An unstructured notes block that can be attached to an artifact."""
74
+ """An unstructured notes block that can be attached to a record."""
75
75
 
76
76
  class Meta:
77
77
  app_label = "lamindb"
@@ -91,7 +91,7 @@ class ArtifactBlock(BlockMixin, BaseSQLRecord):
91
91
 
92
92
 
93
93
  class TransformBlock(BlockMixin, BaseSQLRecord):
94
- """An unstructured notes block that can be attached to an artifact."""
94
+ """An unstructured notes block that can be attached to a transform."""
95
95
 
96
96
  class Meta:
97
97
  app_label = "lamindb"
@@ -105,7 +105,7 @@ class TransformBlock(BlockMixin, BaseSQLRecord):
105
105
 
106
106
 
107
107
  class RunBlock(BlockMixin, BaseSQLRecord):
108
- """An unstructured notes block that can be attached to an artifact."""
108
+ """An unstructured notes block that can be attached to a run."""
109
109
 
110
110
  class Meta:
111
111
  app_label = "lamindb"
@@ -115,7 +115,7 @@ class RunBlock(BlockMixin, BaseSQLRecord):
115
115
 
116
116
 
117
117
  class CollectionBlock(BlockMixin, BaseSQLRecord):
118
- """An unstructured notes block that can be attached to an artifact."""
118
+ """An unstructured notes block that can be attached to a collection."""
119
119
 
120
120
  class Meta:
121
121
  app_label = "lamindb"
@@ -127,7 +127,7 @@ class CollectionBlock(BlockMixin, BaseSQLRecord):
127
127
 
128
128
 
129
129
  class SchemaBlock(BlockMixin, BaseSQLRecord):
130
- """An unstructured notes block that can be attached to an artifact."""
130
+ """An unstructured notes block that can be attached to a schema."""
131
131
 
132
132
  class Meta:
133
133
  app_label = "lamindb"
@@ -137,7 +137,7 @@ class SchemaBlock(BlockMixin, BaseSQLRecord):
137
137
 
138
138
 
139
139
  class FeatureBlock(BlockMixin, BaseSQLRecord):
140
- """An unstructured notes block that can be attached to an artifact."""
140
+ """An unstructured notes block that can be attached to a feature."""
141
141
 
142
142
  class Meta:
143
143
  app_label = "lamindb"
@@ -147,16 +147,17 @@ class FeatureBlock(BlockMixin, BaseSQLRecord):
147
147
 
148
148
 
149
149
  class ProjectBlock(BlockMixin, BaseSQLRecord):
150
- """An unstructured notes block that can be attached to an artifact."""
150
+ """An unstructured notes block that can be attached to a project."""
151
151
 
152
152
  class Meta:
153
153
  app_label = "lamindb"
154
154
 
155
155
  project: Project = ForeignKey(Project, CASCADE, related_name="blocks")
156
+ """The project to which the block is attached."""
156
157
 
157
158
 
158
159
  class SpaceBlock(BlockMixin, BaseSQLRecord):
159
- """An unstructured notes block that can be attached to an artifact."""
160
+ """An unstructured notes block that can be attached to a space."""
160
161
 
161
162
  class Meta:
162
163
  app_label = "lamindb"
@@ -166,9 +167,10 @@ class SpaceBlock(BlockMixin, BaseSQLRecord):
166
167
 
167
168
 
168
169
  class BranchBlock(BlockMixin, BaseSQLRecord):
169
- """An unstructured notes block that can be attached to an artifact."""
170
+ """An unstructured notes block that can be attached to a branch."""
170
171
 
171
172
  class Meta:
172
173
  app_label = "lamindb"
173
174
 
174
175
  branch: Branch = ForeignKey(Branch, CASCADE, related_name="blocks")
176
+ """The branch to which the block is attached."""
@@ -356,7 +356,7 @@ def _add_or_remove_synonyms(
356
356
  from IPython.display import display
357
357
 
358
358
  syns_all = (
359
- record.__class__.filter().exclude(synonyms="").exclude(synonyms=None).all() # type: ignore
359
+ record.__class__.filter().exclude(synonyms="").exclude(synonyms=None) # type: ignore
360
360
  )
361
361
  if len(syns_all) == 0:
362
362
  return
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, Literal, overload
6
6
  import anndata as ad
7
7
  import pandas as pd
8
8
  from django.db import models
9
- from django.db.models import CASCADE, PROTECT
9
+ from django.db.models import CASCADE, PROTECT, Q
10
10
  from lamin_utils import logger
11
11
  from lamindb_setup.core.hashing import HASH_LENGTH, hash_set
12
12
 
@@ -25,11 +25,11 @@ from ..models._is_versioned import process_revises
25
25
  from ._is_versioned import IsVersioned
26
26
  from .artifact import (
27
27
  Artifact,
28
- _populate_subsequent_runs_,
29
- _track_run_input,
30
28
  describe_artifact_collection,
31
29
  get_run,
30
+ populate_subsequent_run,
32
31
  save_schema_links,
32
+ track_run_input,
33
33
  )
34
34
  from .has_parents import view_lineage
35
35
  from .run import Run, TracksRun, TracksUpdates
@@ -317,17 +317,19 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
317
317
  )
318
318
  # we ignore collections in trash containing the same hash
319
319
  if hash is not None:
320
- existing_collection = Collection.filter(hash=hash).one_or_none()
320
+ existing_collection = Collection.objects.filter(
321
+ ~Q(branch_id=-1),
322
+ hash=hash,
323
+ ).one_or_none()
321
324
  else:
322
325
  existing_collection = None
323
326
  if existing_collection is not None:
324
327
  logger.warning(
325
328
  f"returning existing collection with same hash: {existing_collection}; if you intended to query to track this collection as an input, use: ln.Collection.get()"
326
329
  )
327
- if run is not None:
328
- existing_collection._populate_subsequent_runs(run)
329
330
  init_self_from_db(self, existing_collection)
330
331
  update_attributes(self, {"description": description, "key": key})
332
+ populate_subsequent_run(self, run)
331
333
  else:
332
334
  _skip_validation = revises is not None and key == revises.key
333
335
  super().__init__( # type: ignore
@@ -348,9 +350,9 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
348
350
  _skip_validation=_skip_validation,
349
351
  )
350
352
  self._artifacts = artifacts
351
- if revises is not None:
352
- _track_run_input(revises, run=run)
353
- _track_run_input(artifacts, run=run)
353
+ if revises is not None and revises.uid != self.uid:
354
+ track_run_input(revises, run=run)
355
+ track_run_input(artifacts, run=run)
354
356
 
355
357
  @classmethod
356
358
  def get(
@@ -440,7 +442,7 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
440
442
 
441
443
  dataframe = _open_dataframe(paths, engine=engine, **kwargs)
442
444
  # track only if successful
443
- _track_run_input(self, is_run_input)
445
+ track_run_input(self, is_run_input)
444
446
  return dataframe
445
447
 
446
448
  def mapped(
@@ -540,7 +542,7 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
540
542
  dtype,
541
543
  )
542
544
  # track only if successful
543
- _track_run_input(self, is_run_input)
545
+ track_run_input(self, is_run_input)
544
546
  return ds
545
547
 
546
548
  def cache(self, is_run_input: bool | None = None) -> list[UPath]:
@@ -557,7 +559,7 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
557
559
  for artifact in self.ordered_artifacts.all():
558
560
  # do not want to track data lineage on the artifact level
559
561
  path_list.append(artifact.cache(is_run_input=False))
560
- _track_run_input(self, is_run_input)
562
+ track_run_input(self, is_run_input)
561
563
  return path_list
562
564
 
563
565
  def load(
@@ -570,11 +572,11 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
570
572
 
571
573
  Returns an in-memory concatenated `DataFrame` or `AnnData` object.
572
574
  """
573
- # cannot call _track_run_input here, see comment further down
575
+ # cannot call track_run_input here, see comment further down
574
576
  artifacts = self.ordered_artifacts.all()
575
577
  concat_object = _load_concat_artifacts(artifacts, join, **kwargs)
576
578
  # only call it here because there might be errors during load or concat
577
- _track_run_input(self, is_run_input)
579
+ track_run_input(self, is_run_input)
578
580
  return concat_object
579
581
 
580
582
  def save(self, using: str | None = None) -> Collection:
@@ -664,9 +666,6 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
664
666
  """
665
667
  return describe_artifact_collection(self)
666
668
 
667
- def _populate_subsequent_runs(self, run: Run) -> None:
668
- _populate_subsequent_runs_(self, run)
669
-
670
669
 
671
670
  # internal function, not exposed to user
672
671
  def from_artifacts(artifacts: Iterable[Artifact]) -> tuple[str, dict[str, str]]:
@@ -550,9 +550,7 @@ def _get_all_child_runs(data: Artifact | Collection) -> list:
550
550
  runs.update(
551
551
  {
552
552
  f.run
553
- for f in data.run.output_collections.all()
554
- .filter(branch_id__in=[0, 1])
555
- .all()
553
+ for f in data.run.output_collections.all().filter(branch_id__in=[0, 1])
556
554
  }
557
555
  )
558
556
  while runs.difference(all_runs):
@@ -237,7 +237,7 @@ class QueryManager(Manager):
237
237
  Examples:
238
238
 
239
239
  >>> ln.save(ln.ULabel.from_values(["ULabel1", "ULabel2", "ULabel3"], field="name")) # noqa
240
- >>> labels = ln.ULabel.filter(name__icontains = "label").all()
240
+ >>> labels = ln.ULabel.filter(name__icontains = "label")
241
241
  >>> ln.ULabel(name="ULabel1").save()
242
242
  >>> label = ln.ULabel.get(name="ULabel1")
243
243
  >>> label.parents.set(labels)
@@ -245,7 +245,7 @@ class QueryManager(Manager):
245
245
  >>> manager.to_dataframe()
246
246
  """
247
247
 
248
- def _track_run_input_manager(self):
248
+ def track_run_input_manager(self):
249
249
  if hasattr(self, "source_field_name") and hasattr(self, "target_field_name"):
250
250
  if (
251
251
  self.source_field_name == "collection"
@@ -255,7 +255,7 @@ class QueryManager(Manager):
255
255
  from lamindb.core._context import context
256
256
  from lamindb.models.artifact import (
257
257
  WARNING_RUN_TRANSFORM,
258
- _track_run_input,
258
+ track_run_input,
259
259
  )
260
260
 
261
261
  if (
@@ -263,14 +263,14 @@ class QueryManager(Manager):
263
263
  and not settings.creation.artifact_silence_missing_run_warning
264
264
  ):
265
265
  logger.warning(WARNING_RUN_TRANSFORM)
266
- _track_run_input(self.instance)
266
+ track_run_input(self.instance)
267
267
 
268
268
  def to_list(self, field: str | None = None):
269
269
  """Populate a list with the results.
270
270
 
271
271
  Examples:
272
272
  >>> ln.save(ln.ULabel.from_values(["ULabel1", "ULabel2", "ULabel3"], field="name"))
273
- >>> labels = ln.ULabel.filter(name__icontains="label").all()
273
+ >>> labels = ln.ULabel.filter(name__icontains="label")
274
274
  >>> ln.ULabel(name="ULabel1").save()
275
275
  >>> label = ln.ULabel.get(name="ULabel1")
276
276
  >>> label.parents.set(labels)
@@ -281,7 +281,7 @@ class QueryManager(Manager):
281
281
  if field is None:
282
282
  return list(self.all())
283
283
  else:
284
- self._track_run_input_manager()
284
+ self.track_run_input_manager()
285
285
  return list(self.values_list(field, flat=True))
286
286
 
287
287
  @deprecated(new_name="to_list")
@@ -304,7 +304,7 @@ class QueryManager(Manager):
304
304
 
305
305
  For `**kwargs`, see :meth:`lamindb.models.QuerySet.to_dataframe`.
306
306
  """
307
- self._track_run_input_manager()
307
+ self.track_run_input_manager()
308
308
  return super().all()
309
309
 
310
310
  @doc_args(_search.__doc__)
@@ -26,6 +26,8 @@ from .sqlrecord import Registry, SQLRecord
26
26
  if TYPE_CHECKING:
27
27
  from lamindb.base.types import ListLike, StrField
28
28
 
29
+ from .sqlrecord import Branch
30
+
29
31
  T = TypeVar("T")
30
32
 
31
33
 
@@ -60,7 +62,7 @@ def get_keys_from_df(data: list, registry: SQLRecord) -> list[str]:
60
62
  return keys
61
63
 
62
64
 
63
- def get_default_branch_ids() -> list[int]:
65
+ def get_default_branch_ids(branch: Branch | None = None) -> list[int]:
64
66
  """Return branch IDs to include in default queries.
65
67
 
66
68
  By default, queries include records on the main branch (branch_id=1) but exclude trashed (branch_id=-1)
@@ -72,7 +74,10 @@ def get_default_branch_ids() -> list[int]:
72
74
  Returns:
73
75
  List containing the default branch and current branch if different.
74
76
  """
75
- branch_id = setup_settings.branch.id
77
+ if branch is None:
78
+ branch_id = setup_settings.branch.id
79
+ else:
80
+ branch_id = branch.id
76
81
  branch_ids = [branch_id]
77
82
  if branch_id != 1: # add the main branch by default
78
83
  branch_ids.append(1)
@@ -142,7 +147,7 @@ def get_backward_compat_filter_kwargs(queryset, expressions):
142
147
  return list(mapped.keys()) if was_list else mapped
143
148
 
144
149
 
145
- def process_expressions(queryset: QuerySet, expressions: dict) -> dict:
150
+ def process_expressions(queryset: QuerySet, queries: tuple, expressions: dict) -> dict:
146
151
  def _map_databases(value: Any, key: str, target_db: str) -> tuple[str, Any]:
147
152
  if isinstance(value, SQLRecord):
148
153
  if value._state.db != target_db:
@@ -170,6 +175,28 @@ def process_expressions(queryset: QuerySet, expressions: dict) -> dict:
170
175
 
171
176
  return key, value
172
177
 
178
+ branch_fields = {"branch", "branch_id"}
179
+ branch_prefixes = ("branch__", "branch_id__")
180
+
181
+ def queries_contain_branch(queries: tuple) -> bool:
182
+ """Check if any Q object in queries references branch or branch_id."""
183
+
184
+ def check_q_object(q: Q) -> bool:
185
+ # Q objects store their conditions in q.children
186
+ for child in q.children:
187
+ if isinstance(child, tuple) and len(child) == 2:
188
+ # Normal condition: (key, value)
189
+ key = child[0]
190
+ if key in branch_fields or key.startswith(branch_prefixes):
191
+ return True
192
+ elif isinstance(child, Q):
193
+ # Nested Q object
194
+ if check_q_object(child):
195
+ return True
196
+ return False
197
+
198
+ return any(check_q_object(q) for q in queries if isinstance(q, Q))
199
+
173
200
  expressions = get_backward_compat_filter_kwargs(
174
201
  queryset,
175
202
  expressions,
@@ -179,15 +206,13 @@ def process_expressions(queryset: QuerySet, expressions: dict) -> dict:
179
206
  id_uid_hash = {"id", "uid", "hash", "id__in", "uid__in", "hash__in"}
180
207
  if not any(expression in id_uid_hash for expression in expressions):
181
208
  expressions_have_branch = False
182
- branch_branch_id = {"branch", "branch_id"}
183
- branch_branch_id__ = ("branch__", "branch_id__")
184
209
  for expression in expressions:
185
- if expression in branch_branch_id or expression.startswith(
186
- branch_branch_id__
210
+ if expression in branch_fields or expression.startswith(
211
+ branch_prefixes
187
212
  ):
188
213
  expressions_have_branch = True
189
214
  break
190
- if not expressions_have_branch:
215
+ if not expressions_have_branch and not queries_contain_branch(queries):
191
216
  expressions["branch_id__in"] = get_default_branch_ids()
192
217
  else:
193
218
  # if branch_id is None, do not apply a filter
@@ -197,6 +222,7 @@ def process_expressions(queryset: QuerySet, expressions: dict) -> dict:
197
222
  expressions.pop("branch_id")
198
223
  if "branch" in expressions and expressions["branch"] is None:
199
224
  expressions.pop("branch")
225
+
200
226
  if queryset._db is not None:
201
227
  # only check for database mismatch if there is a defined database on the
202
228
  # queryset
@@ -254,7 +280,7 @@ def get(
254
280
  return one_helper(qs, DOESNOTEXIST_MSG)
255
281
  else:
256
282
  assert idlike is None # noqa: S101
257
- expressions = process_expressions(qs, expressions)
283
+ expressions = process_expressions(qs, [], expressions)
258
284
  # inject is_latest for consistency with idlike
259
285
  is_latest_was_not_in_expressions = "is_latest" not in expressions
260
286
  if issubclass(registry, IsVersioned) and is_latest_was_not_in_expressions:
@@ -1022,11 +1048,11 @@ class QuerySet(BasicQuerySet):
1022
1048
  raise # pragma: no cover
1023
1049
 
1024
1050
  if is_run_input is not False: # might be None or True or Run
1025
- from .artifact import Artifact, _track_run_input
1051
+ from .artifact import Artifact, track_run_input
1026
1052
  from .collection import Collection
1027
1053
 
1028
1054
  if isinstance(record, (Artifact, Collection)):
1029
- _track_run_input(record, is_run_input)
1055
+ track_run_input(record, is_run_input)
1030
1056
 
1031
1057
  return record
1032
1058
 
@@ -1059,7 +1085,7 @@ class QuerySet(BasicQuerySet):
1059
1085
  f"Invalid lookup '{value}' for {field}. Did you mean {field}__name?"
1060
1086
  )
1061
1087
 
1062
- expressions = process_expressions(self, expressions)
1088
+ expressions = process_expressions(self, queries, expressions)
1063
1089
  # need to run a query if queries or expressions are not empty
1064
1090
  if queries or expressions:
1065
1091
  try:
lamindb/models/run.py CHANGED
@@ -9,7 +9,6 @@ from django.db.models import (
9
9
  )
10
10
  from lamindb_setup import _check_instance_setup
11
11
 
12
- from lamindb.base import deprecated
13
12
  from lamindb.base.fields import (
14
13
  BooleanField,
15
14
  CharField,
@@ -198,31 +197,57 @@ class Run(SQLRecord):
198
197
 
199
198
  Args:
200
199
  transform: `Transform` A :class:`~lamindb.Transform` record.
201
- initiated_by_run: `Run | None = None` The run that triggers this run.
200
+ name: `str | None = None` An optional name.
201
+ params: `dict | None = None` A dictionary of parameters.
202
202
  reference: `str | None = None` For instance, an external ID or a download URL.
203
203
  reference_type: `str | None = None` For instance, `redun_id`, `nextflow_id` or `url`.
204
+ initiated_by_run: `Run | None = None` The run that triggers this run.
204
205
 
205
206
  See Also:
206
207
  :func:`~lamindb.track`
207
208
  Globally track a script or notebook run.
209
+ :func:`~lamindb.tracked`
210
+ Track a function with this decorator.
208
211
 
209
212
  Examples:
210
213
 
211
- Create a run record:
214
+ Create a run record::
215
+
216
+ ln.Transform(key="Cell Ranger", version="7.2.0", type="pipeline").save()
217
+ transform = ln.Transform.get(key="Cell Ranger", version="7.2.0")
218
+ run = ln.Run(transform)
219
+
220
+ Create a global run context for a custom transform::
221
+
222
+ ln.track(transform=transform)
223
+ ln.context.run # global run
212
224
 
213
- >>> ln.Transform(key="Cell Ranger", version="7.2.0", type="pipeline").save()
214
- >>> transform = ln.Transform.get(key="Cell Ranger", version="7.2.0")
215
- >>> run = ln.Run(transform)
225
+ Track a global run context for a notebook or script::
216
226
 
217
- Create a global run context for a custom transform:
227
+ ln.track()
228
+ ln.context.run # global run
229
+
230
+ You can pass parameters to `Run(transform, params=params)` or add them later::
231
+
232
+ run.params = {
233
+ "learning_rate": 0.01,
234
+ "input_dir": "s3://my-bucket/mydataset",
235
+ "downsample": True,
236
+ "preprocess_params": {
237
+ "normalization_type": "cool",
238
+ "subset_highlyvariable": True,
239
+ },
240
+ }
241
+ run.save()
218
242
 
219
- >>> ln.track(transform=transform)
220
- >>> ln.context.run # globally available run
243
+ In contrast to `.params`, features are indexed in the `Feature` registry and can reference relational categorical values.
244
+ If you want to link feature values, use::
221
245
 
222
- Track a global run context for a notebook or script:
246
+ run.features.add_values({
247
+ "experiment": "My experiment 1",
248
+ })
223
249
 
224
- >>> ln.track() # Jupyter notebook metadata is automatically parsed
225
- >>> ln.context.run
250
+ Guide: :ref:`track-run-parameters`
226
251
  """
227
252
 
228
253
  class Meta:
@@ -282,7 +307,8 @@ class Run(SQLRecord):
282
307
  """The collections serving as input for this run."""
283
308
  output_records: Record
284
309
  """The collections generated by this run."""
285
- """Parameter values."""
310
+ params: dict = models.JSONField(null=True)
311
+ """JSON-like parameters."""
286
312
  _feature_values: FeatureValue = models.ManyToManyField(
287
313
  "FeatureValue", through="RunFeatureValue", related_name="runs"
288
314
  )
@@ -331,6 +357,8 @@ class Run(SQLRecord):
331
357
  def __init__(
332
358
  self,
333
359
  transform: Transform,
360
+ name: str | None = None,
361
+ params: dict | None = None,
334
362
  reference: str | None = None,
335
363
  reference_type: str | None = None,
336
364
  initiated_by_run: Run | None = None,
@@ -356,6 +384,8 @@ class Run(SQLRecord):
356
384
  transform: Transform = None
357
385
  if "transform" in kwargs or len(args) == 1:
358
386
  transform = kwargs.pop("transform") if len(args) == 0 else args[0]
387
+ name: str | None = kwargs.pop("name", None)
388
+ params: dict | None = kwargs.pop("params", None)
359
389
  reference: str | None = kwargs.pop("reference", None)
360
390
  reference_type: str | None = kwargs.pop("reference_type", None)
361
391
  initiated_by_run: Run | None = kwargs.pop("initiated_by_run", None)
@@ -363,12 +393,17 @@ class Run(SQLRecord):
363
393
  raise TypeError("Pass transform parameter")
364
394
  if transform._state.adding:
365
395
  raise ValueError("Please save transform record before creating a run")
366
-
396
+ if not len(kwargs) == 0:
397
+ raise ValueError(
398
+ f"Only transform, name, params, reference, reference_type, initiated_by_run can be passed, but you passed: {kwargs}"
399
+ )
367
400
  super().__init__( # type: ignore
368
401
  transform=transform,
402
+ name=name,
403
+ params=params,
369
404
  reference=reference,
370
- initiated_by_run=initiated_by_run,
371
405
  reference_type=reference_type,
406
+ initiated_by_run=initiated_by_run,
372
407
  )
373
408
 
374
409
  @property
@@ -377,22 +412,13 @@ class Run(SQLRecord):
377
412
 
378
413
  Returns the status as a string, one of: `scheduled`, `re-started`, `started`, `completed`, `errored`, or `aborted`.
379
414
 
380
- The string maps to an integer field `_status_code` of the run registry, with mapping:
381
- - -3: `scheduled`
382
- - -2: `re-started`
383
- - -1: `started`
384
- - 0: `completed`
385
- - 1: `errored`
386
- - 2: `aborted`
387
-
388
- You can use this private integer field for queries.
389
-
390
415
  Examples:
391
416
 
392
- ::
417
+ See the status of a run::
393
418
 
394
419
  run.status
395
420
  #> 'completed'
421
+
396
422
  """
397
423
  if self._status_code is None:
398
424
  return "unknown"
@@ -406,31 +432,9 @@ class Run(SQLRecord):
406
432
  }
407
433
  return status_dict.get(self._status_code, "unknown")
408
434
 
409
- @property
410
- @deprecated("features")
411
- def params(self) -> FeatureManager:
412
- return self.features
413
-
414
435
  @property
415
436
  def features(self) -> FeatureManager:
416
- """Features manager.
417
-
418
- Run parameters are tracked via the `Feature` registry, just like all other variables.
419
-
420
- Guide: :ref:`track-run-parameters`
421
-
422
- Example::
423
-
424
- run.features.add_values({
425
- "learning_rate": 0.01,
426
- "input_dir": "s3://my-bucket/mydataset",
427
- "downsample": True,
428
- "preprocess_params": {
429
- "normalization_type": "cool",
430
- "subset_highlyvariable": True,
431
- },
432
- })
433
- """
437
+ """Manage annotations with features."""
434
438
  from ._feature_manager import FeatureManager
435
439
 
436
440
  return FeatureManager(self)