pyspiral 0.6.4__cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl → 0.6.5__cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyspiral
3
- Version: 0.6.4
3
+ Version: 0.6.5
4
4
  Classifier: Intended Audience :: Science/Research
5
5
  Classifier: Operating System :: OS Independent
6
6
  Classifier: Programming Language :: Python
@@ -1,8 +1,8 @@
1
- pyspiral-0.6.4.dist-info/METADATA,sha256=NHgJTgogXMcDIHbb3I6ONsKdj23IfS2Vwa60m6YgGZo,1842
2
- pyspiral-0.6.4.dist-info/WHEEL,sha256=sHl2MPySRQtLBS4t9I9tl1bAeFFBhTGABHYdwnegkVM,130
3
- pyspiral-0.6.4.dist-info/entry_points.txt,sha256=uft7u-a6g40NLt4Q6BleWbK4NY0M8nZuYPpP8DV0EOk,45
1
+ pyspiral-0.6.5.dist-info/METADATA,sha256=3oqXIVdsaDE1PmfUgS5M4zyoSiVWlcS9718MClPjQQw,1842
2
+ pyspiral-0.6.5.dist-info/WHEEL,sha256=sHl2MPySRQtLBS4t9I9tl1bAeFFBhTGABHYdwnegkVM,130
3
+ pyspiral-0.6.5.dist-info/entry_points.txt,sha256=uft7u-a6g40NLt4Q6BleWbK4NY0M8nZuYPpP8DV0EOk,45
4
4
  spiral/__init__.py,sha256=5c0faqg-kHZBDwriQ7LzLAMcFolIucp-IA1EzNvCZ3k,711
5
- spiral/_lib.abi3.so,sha256=oq_VegFDXsy3-ap1Coy5wf_JHD4s6s8PZ8VguB65bmQ,50403064
5
+ spiral/_lib.abi3.so,sha256=XQ5Tfs9s0f7TrMkrc5It8n6C__b_AWLgfCf-cpL5K-s,57866344
6
6
  spiral/adbc.py,sha256=7IxfWIeQN-fh0W5OdN_PP2x3pzQYg6ZUOLsHg3jktqw,14842
7
7
  spiral/api/__init__.py,sha256=ULBlVq3PnfNOO6T5naE_ULmmii-83--qTuN2PpAUQN0,2241
8
8
  spiral/api/admin.py,sha256=A1iVR1XYJSObZivPAD5UzmPuMgupXc9kaHNYYa_kwfs,585
@@ -10,7 +10,7 @@ spiral/api/client.py,sha256=xGc3RKRrerDGGt3QA7Y_friEa-OkZXSQI2Yd1KeFDdw,4668
10
10
  spiral/api/filesystems.py,sha256=yEHgHfo7t1_becm0UFedc3nd49_G77hHjYwtYQ6P9XU,4240
11
11
  spiral/api/key_space_indexes.py,sha256=-38rZXTdkL4mLhp9h3CtqyIyutzzq88tV6bhK05MqYE,640
12
12
  spiral/api/organizations.py,sha256=B-8zZ7lFJANGK7dUNbo_aU-cgI959JBP9VcWb6wdgi0,1895
13
- spiral/api/projects.py,sha256=62Y1lqI_TpUh3WKQqrjbLWJHiZsI_X3g8u2RTbUwkoA,6162
13
+ spiral/api/projects.py,sha256=1JC7VjqZJfwR6zfhBZr3OCwaf6zb-MXMOBTE_NztmcE,6356
14
14
  spiral/api/telemetry.py,sha256=tfdA3E_EWJwFVxkQfkm8tiYGRubnx2LuE5nbfsk1oG4,474
15
15
  spiral/api/text_indexes.py,sha256=_zVlGBytl-9-Unbu2POfZgLh40H1YRcagFtplgIG428,1828
16
16
  spiral/api/types.py,sha256=lGdiKViRgIEJXD2ubwnyEIEwHkfRumlZjVEaHMV3Tm8,682
@@ -25,21 +25,21 @@ spiral/cli/console.py,sha256=6JHbAQV6MFWz3P-VzqPOjhHpkIQagsCdzTMvmuDKMkU,2580
25
25
  spiral/cli/fs.py,sha256=vaPcSc2YghhHeipxNitIdsHaBhFwlwkvPFqYsFSN9P0,2927
26
26
  spiral/cli/iceberg.py,sha256=Q14tcGcn1LixbFCYP0GhfYwFFXTmmi8tqBPYwalJEyE,3248
27
27
  spiral/cli/key_spaces.py,sha256=x3IFRP5d47pKiAHeWExYMOBaT2TwxbWjVM01SUqKrwI,2943
28
- spiral/cli/login.py,sha256=iyWQR2n2cOcg2-6NMaD2uCSQfvsoz7wMeyT9s7h80Fc,698
28
+ spiral/cli/login.py,sha256=2tw6uN5rEpiMMAmjQSB3-JUPf3C0Wc1eTGCDxhYtJps,731
29
29
  spiral/cli/orgs.py,sha256=fmOuLxpeIFfKqePRi292Gv9k-EF5pPn_tbKd2BLl2Ig,2869
30
30
  spiral/cli/printer.py,sha256=HcvSUpaMItzmhBUfIHROK1Z3SL8J8wDopS3Qo8H00uw,1781
31
- spiral/cli/projects.py,sha256=UYrBlLcFacuXExdLX1sZByfvkz9MRtk_0oRAZvqHa0w,5105
31
+ spiral/cli/projects.py,sha256=1M1nGrBT-t0aY9RV5Cnmzy7YrhIvmHwdkpa3y9j8rG8,5756
32
32
  spiral/cli/state.py,sha256=10wTIVQ0SJkY67Z6-KQ1LFlt3aVIPmZhoHFdTwp4kNA,130
33
33
  spiral/cli/tables.py,sha256=48lZ0wPQSCTul1vn-Qx6Be5eGnw75Abtw2zxMK9dCPg,4613
34
34
  spiral/cli/telemetry.py,sha256=Uxo1Q1FkKJ6n6QNGOUmL3j_pRRWRx0qWIhoP-U9BuR0,589
35
35
  spiral/cli/text.py,sha256=DlWGe4JrkdERAiqyITNpk91Wqb63Re99rNYlIFsIamc,4031
36
36
  spiral/cli/types.py,sha256=XYzo1GgX7dBBItoBSrHI4vO5C2lLmS2sktb-2GnGH3E,1362
37
37
  spiral/cli/workloads.py,sha256=2_SLfQTFN6y73R9H0i9dk8VIOVagKxSxOpHXC56yptY,2015
38
- spiral/client.py,sha256=Po9xgCH3NwVsCeRZMm3eJUPV77Rknyj-9MfCS1TbdTg,6623
38
+ spiral/client.py,sha256=ANKzL6yQ-7X4Livq4xCZsWVdQSU3j1J7gSJv6dzeaJs,6630
39
39
  spiral/core/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  spiral/core/_tools/__init__.pyi,sha256=b2KLfTOQ67pjfbYt07o0IGiTu5o2bZw69lllV8v0Dps,143
41
- spiral/core/authn/__init__.pyi,sha256=Jw_8ywTMDTwgAtGxMtFED63rU0jOgrv-eZtaZ5sR5t4,757
42
- spiral/core/client/__init__.pyi,sha256=6D4eu78eHW9Zl5Fx3UIRbk9ywvlZJephal56H1LDjko,6095
41
+ spiral/core/authn/__init__.pyi,sha256=z_GWyIS62fuiYQrYO8hzw4W8oGaiciqS1u5qtAt54VY,769
42
+ spiral/core/client/__init__.pyi,sha256=wEZi15Hf7Jg-Qo1N-k97ss1YqwSQehuoZVqPxj_4EvA,6126
43
43
  spiral/core/table/__init__.pyi,sha256=ajxO2N92hTQ4evsl7QBWB8ivz-cDNxXnAv0jytRw0ZY,3183
44
44
  spiral/core/table/manifests/__init__.pyi,sha256=eVfDpmhYSjafIvvALqAkZe5baN3Y1HpKpxYEbjwd4gQ,1043
45
45
  spiral/core/table/metastore/__init__.pyi,sha256=rc3u9MwEKRvL2kxOc8lBorddFRnM8o_o1frqtae86a4,1697
@@ -81,21 +81,21 @@ spiral/protogen/_/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  spiral/protogen/_/scandal/__init__.py,sha256=liUQAICLd2sPccCmqo0_c1duSbNj_m8p_IgmdnHsB3E,4965
82
82
  spiral/protogen/_/spfs/__init__.py,sha256=zMMEDIfPXQNBkisLI-iMWbJABye-vK42Gf2BUQQYR_c,2028
83
83
  spiral/protogen/_/spql/__init__.py,sha256=PEC4bI-PHdJ4Zd8Jb1k6Xk2iFYoYqIUbTGlL2JVGnT0,1548
84
- spiral/protogen/_/substrait/__init__.py,sha256=eVv-5CRJv8KstANM-_U3WzCVmkmZ8_BTOFP-1f2sSX4,209839
84
+ spiral/protogen/_/substrait/__init__.py,sha256=-ngqHcYfio6s1B4M1_e1VsDymUcFK9qdM17ECA31qLw,209837
85
85
  spiral/protogen/_/substrait/extensions/__init__.py,sha256=nhnEnho70GAT8WPj2xtwJUzk5GJ6X2e-HTvyk7emGsk,5326
86
86
  spiral/protogen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
87
  spiral/protogen/util.py,sha256=smnvVo6nYH3FfDm9jqhNLaXz4bbTBaQezHQDCTvZyiQ,1486
88
88
  spiral/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
89
  spiral/scan.py,sha256=SCDZ9UXA4g0Jq9BQ9Zt7cEK2NBq64Hqh_SttR4tF6jo,6252
90
90
  spiral/server.py,sha256=ztBmB5lBnUz-smQxR_tC8AI5SOhz17wH0MI3GuzDUdM,600
91
- spiral/settings.py,sha256=cLD1MSyzIM1CRRB2ncYB9u849cxGAqYlycvNExU6TGo,3096
91
+ spiral/settings.py,sha256=JRQSwjJyNaCqQdQLxiqB_O_LZRQXMLyshJBrI2LZHwM,3113
92
92
  spiral/snapshot.py,sha256=tYEIKYYqS9Eusb8rsrG46VD7fiNPA9yVOR5ajMMtT_g,2018
93
93
  spiral/streaming_/__init__.py,sha256=s7MlW2ERsuZmZGExLFL6RcZon2e0tNBocBg5ANgki7k,61
94
94
  spiral/streaming_/reader.py,sha256=Kpqknv2jn12jUhHOEEDArj0JZwrWb8XjoOGs9HrdVyA,4047
95
95
  spiral/streaming_/stream.py,sha256=-prGp73h0XDsdKW0mAEamy4AXhd1oF5fBbNbbY1k2-A,5931
96
96
  spiral/substrait_.py,sha256=AKeOD4KIXvz2J4TYxnIneOiHddtBIyOhuNxVO_uH0eg,12592
97
- spiral/table.py,sha256=ru1G7CXZGD-k4sg621qe-IEAU9kU1WujZ32AXAmdvx4,8861
97
+ spiral/table.py,sha256=iWoWm0XAcmW99NQXgEsdDWvq1Z9tUSd6t0kn4o1WmSo,9019
98
98
  spiral/text_index.py,sha256=FQ9rgIEGLSJryS9lFdMhKtPFey18BXoWbPXyvZPJJ04,442
99
- spiral/transaction.py,sha256=nSykH4UGs9hGtWuSWK9YyT9jfEuvzfkKoUgMM5Xt4zU,1841
99
+ spiral/transaction.py,sha256=rjoOQc9bvs1zAAhb8cLtJOOn2146UAMZjcSVcEXeNFY,1839
100
100
  spiral/types_.py,sha256=W_jyO7F6rpPiH69jhgSgV7OxQZbOlb1Ho3InpKUP6Eo,155
101
- pyspiral-0.6.4.dist-info/RECORD,,
101
+ pyspiral-0.6.5.dist-info/RECORD,,
spiral/_lib.abi3.so CHANGED
Binary file
spiral/api/projects.py CHANGED
@@ -99,19 +99,26 @@ class ModalPrincipalConditions(BaseModel):
99
99
  conditions: ModalConditions | None = None
100
100
 
101
101
 
102
- class GCPPrincipalConditions(BaseModel):
102
+ class GcpServiceAccountPrincipalConditions(BaseModel):
103
103
  type: Literal["gcp"] = "gcp"
104
104
  service_account: str
105
105
  unique_id: str
106
106
 
107
107
 
108
+ class AwsAssumedRolePrincipalConditions(BaseModel):
109
+ type: Literal["aws"] = "aws"
110
+ account_id: str
111
+ role_name: str
112
+
113
+
108
114
  PrincipalConditions = Annotated[
109
115
  OrgRolePrincipalConditions
110
116
  | OrgUserPrincipalConditions
111
117
  | WorkloadPrincipalConditions
112
118
  | GitHubPrincipalConditions
113
119
  | ModalPrincipalConditions
114
- | GCPPrincipalConditions,
120
+ | GcpServiceAccountPrincipalConditions
121
+ | AwsAssumedRolePrincipalConditions,
115
122
  Field(discriminator="type"),
116
123
  ]
117
124
 
spiral/cli/login.py CHANGED
@@ -13,7 +13,9 @@ def command(org_id: str | None = None, force: bool = False, show_token: bool = F
13
13
  def whoami():
14
14
  """Display the current user's information."""
15
15
  payload = jwt.decode(state.settings.authn.token().expose_secret(), options={"verify_signature": False})
16
- CONSOLE.print(f"{payload['org_id']}")
16
+
17
+ if "org_id" in payload:
18
+ CONSOLE.print(f"{payload['org_id']}")
17
19
  CONSOLE.print(f"{payload['sub']}")
18
20
 
19
21
 
spiral/cli/projects.py CHANGED
@@ -1,12 +1,13 @@
1
- from typing import Annotated
1
+ from typing import Annotated, Literal
2
2
 
3
3
  import typer
4
4
  from typer import Option
5
5
 
6
6
  from spiral.api.organizations import OrgRole
7
7
  from spiral.api.projects import (
8
+ AwsAssumedRolePrincipalConditions,
8
9
  CreateProjectRequest,
9
- GCPPrincipalConditions,
10
+ GcpServiceAccountPrincipalConditions,
10
11
  GitHubConditions,
11
12
  GitHubPrincipalConditions,
12
13
  Grant,
@@ -41,40 +42,47 @@ def create(
41
42
  CONSOLE.print(f"Created project {res.project.id}")
42
43
 
43
44
 
44
- @app.command(help="Grant a role on a project.")
45
+ @app.command(help="Grant a role on a project to a principal.")
45
46
  def grant(
46
47
  project: ProjectArg,
47
- role: Annotated[str, Option(help="Role to grant.")],
48
+ role: Annotated[Literal["viewer", "editor", "admin"], Option(help="Project role to grant.")],
48
49
  org_id: Annotated[
49
50
  str | None, Option(help="Pass an organization ID to grant a role to an organization user(s).")
50
51
  ] = None,
51
- user_id: Annotated[
52
+ org_user: Annotated[
52
53
  str | None, Option(help="Pass a user ID when using --org-id to grant a role to grant a role to a user.")
53
54
  ] = None,
54
55
  org_role: Annotated[
55
- str | None,
56
- Option(help="Pass an organization role when using --org-id to grant a role to all users with that role."),
56
+ Literal["owner", "member", "guest"] | None,
57
+ Option(help="Pass an org role when using --org-id to grant a role to all users with that role."),
57
58
  ] = None,
58
59
  workload_id: Annotated[str | None, Option(help="Pass a workload ID to grant a role to a workload.")] = None,
59
60
  github: Annotated[
60
- str | None, Option(help="Pass an `<org>/<repo>` string to grant a role to a job running in GitHub Actions.")
61
+ str | None, Option(help="Pass an `{org}/{repo}` string to grant a role to a job running in GitHub Actions.")
61
62
  ] = None,
62
63
  modal: Annotated[
63
64
  str | None,
64
- Option(help="Pass a `<workspace_id>/<env_name>` string to grant a role to a job running in Modal environment."),
65
+ Option(help="Pass a `{workspace_id}/{env_name}` string to grant a role to a job running in Modal environment."),
66
+ ] = None,
67
+ gcp_service_account: Annotated[
68
+ str | None,
69
+ Option(help="Pass a `{service_account_email}/{unique_id}` to grant a role to a GCP service account."),
65
70
  ] = None,
66
- gcp: Annotated[
71
+ aws_iam_role: Annotated[
67
72
  str | None,
68
- Option(help="Pass a `<service_account_email>/<unique_id>` to grant a role to a GCP service account."),
73
+ Option(help="Pass a `{account_id}/{role_name}` to grant a Spiral role to an AWS IAM Role."),
69
74
  ] = None,
70
75
  conditions: list[str] | None = Option(
71
76
  default=None,
72
- help="`<key>=<value>` token conditions to apply to the grant when using --github or --modal.",
77
+ help="`{key}={value}` token conditions to apply to the grant",
73
78
  ),
74
79
  ):
75
80
  # Check mutual exclusion
76
- if sum(int(bool(opt)) for opt in {org_id, workload_id, github, modal, gcp}) != 1:
77
- raise typer.BadParameter("Only one of --org-id, --github or --modal may be specified.")
81
+ if sum(int(bool(opt)) for opt in {org_id, workload_id, github, modal, gcp_service_account, aws_iam_role}) != 1:
82
+ raise typer.BadParameter(
83
+ "Only one of [--org-id, --workload-id, --github, --modal, --gcp-service-account, --aws-iam-role] "
84
+ "may be specified."
85
+ )
78
86
 
79
87
  if github:
80
88
  org, repo = github.split("/", 1)
@@ -98,22 +106,26 @@ def grant(
98
106
 
99
107
  elif org_id:
100
108
  # Check mutual exclusion
101
- if sum(int(bool(opt)) for opt in {user_id, org_role}) != 1:
102
- raise typer.BadParameter("Only one of --user-id or --org-role may be specified.")
109
+ if sum(int(bool(opt)) for opt in {org_user, org_role}) != 1:
110
+ raise typer.BadParameter("Only one of --org-user or --org-role may be specified.")
103
111
 
104
- if user_id is not None:
105
- principal = OrgUserPrincipalConditions(org_id=org_id, user_id=user_id)
112
+ if org_user is not None:
113
+ principal = OrgUserPrincipalConditions(org_id=org_id, user_id=org_user)
106
114
  elif org_role is not None:
107
115
  principal = OrgRolePrincipalConditions(org_id=org_id, role=OrgRole(org_role))
108
116
  else:
109
- raise NotImplementedError("Only user or role principal is supported at this time.")
117
+ raise typer.BadParameter("One of --org-user or --org-role must be specified with --org-id.")
110
118
 
111
119
  elif workload_id:
112
120
  principal = WorkloadPrincipalConditions(workload_id=workload_id)
113
121
 
114
- elif gcp:
115
- service_account, unique_id = gcp.split("/", 1)
116
- principal = GCPPrincipalConditions(service_account=service_account, unique_id=unique_id)
122
+ elif gcp_service_account:
123
+ service_account, unique_id = gcp_service_account.split("/", 1)
124
+ principal = GcpServiceAccountPrincipalConditions(service_account=service_account, unique_id=unique_id)
125
+
126
+ elif aws_iam_role:
127
+ account_id, role_name = aws_iam_role.split("/", 1)
128
+ principal = AwsAssumedRolePrincipalConditions(account_id=account_id, role_name=role_name)
117
129
 
118
130
  else:
119
131
  raise ValueError("Invalid grant principal")
spiral/client.py CHANGED
@@ -98,7 +98,7 @@ class Spiral:
98
98
  *projections: ExprLike,
99
99
  where: ExprLike | None = None,
100
100
  asof: datetime | int | None = None,
101
- exclude_keys: bool = False,
101
+ exclude_keys: bool | None = None,
102
102
  ) -> Scan:
103
103
  """Starts a read transaction on the Spiral.
104
104
 
@@ -136,7 +136,7 @@ class Spiral:
136
136
  filters: ExprLike | None = None,
137
137
  freshness_window: timedelta | None = None,
138
138
  ) -> pa.RecordBatchReader:
139
- """Queries the index with the given rank by and filters clauses. Returns a steam of scored keys.
139
+ """Queries the index with the given rank by and filters clauses. Returns a stream of scored keys.
140
140
 
141
141
  Args:
142
142
  top_k: The number of top results to return.
@@ -8,7 +8,7 @@ class Authn:
8
8
  @staticmethod
9
9
  def from_token(token: Token) -> Authn: ...
10
10
  @staticmethod
11
- def from_fallback() -> Authn: ...
11
+ def from_fallback(api_url: str) -> Authn: ...
12
12
  @staticmethod
13
13
  def from_device() -> Authn: ...
14
14
  def token(self) -> Token | None: ...
@@ -26,12 +26,12 @@ class Spiral:
26
26
  projection: Expr,
27
27
  filter: Expr | None = None,
28
28
  asof: int | None = None,
29
- exclude_keys: bool = False,
29
+ exclude_keys: bool | None = None,
30
30
  ) -> Scan:
31
31
  """Construct a table scan."""
32
32
  ...
33
33
 
34
- def transaction(self, table: Table, format: str | None = None) -> Transaction:
34
+ def transaction(self, table: Table, format: str | None = None, retries: int | None = 3) -> Transaction:
35
35
  """Being a table transaction."""
36
36
  ...
37
37
 
@@ -2885,7 +2885,7 @@ class ExpressionMaskExpression(betterproto2.Message):
2885
2885
  """
2886
2886
  A reference that takes an existing subtype and selectively removes fields
2887
2887
  from it. For example, one might initially have an inner struct with 100
2888
- fields but a a particular operation only needs to interact with only 2 of
2888
+ fields but a particular operation only needs to interact with only 2 of
2889
2889
  those 100 fields. In this situation, one would use a mask expression to
2890
2890
  eliminate the 98 fields that are not relevant to the rest of the operation
2891
2891
  pipeline.
spiral/settings.py CHANGED
@@ -91,7 +91,7 @@ class Settings(BaseSettings):
91
91
  def authn(self):
92
92
  if self.spiraldb.token:
93
93
  return Authn.from_token(self.spiraldb.token)
94
- return Authn.from_fallback()
94
+ return Authn.from_fallback(self.spiraldb.uri)
95
95
 
96
96
  @functools.cached_property
97
97
  def device_code_auth(self) -> DeviceCodeAuth:
spiral/table.py CHANGED
@@ -116,7 +116,7 @@ class Table(Expr):
116
116
 
117
117
 
118
118
  :param column_paths: Fully qualified column names. (e.g., "column_name" or "nested.field").
119
- All columns must exist, if a a column doesn't exist the function will return an error.
119
+ All columns must exist, if a column doesn't exist the function will return an error.
120
120
  """
121
121
  with self.txn() as txn:
122
122
  txn.drop_columns(column_paths)
@@ -127,14 +127,16 @@ class Table(Expr):
127
127
  asof = int(asof.timestamp() * 1_000_000)
128
128
  return Snapshot(self, self.core.get_snapshot(asof=asof))
129
129
 
130
- def txn(self) -> Transaction:
130
+ def txn(self, retries: int | None = 3) -> Transaction:
131
131
  """Begins a new transaction. Transaction must be committed for writes to become visible.
132
132
 
133
+ :param retries: Maximum number of retry attempts on conflict (default: 3). Set to None for a single attempt.
134
+
133
135
  IMPORTANT: While transaction can be used to atomically write data to the table,
134
136
  it is important that the primary key columns are unique within the transaction.
135
137
  The behavior is undefined if this is not the case.
136
138
  """
137
- return Transaction(self.spiral._core.transaction(self.core, settings().file_format))
139
+ return Transaction(self.spiral._core.transaction(self.core, settings().file_format, retries=retries))
138
140
 
139
141
  def to_dataset(self) -> "ds.Dataset":
140
142
  """Returns a PyArrow Dataset representing the table."""
spiral/transaction.py CHANGED
@@ -45,7 +45,7 @@ class Transaction:
45
45
 
46
46
 
47
47
  :param column_paths: Fully qualified column names. (e.g., "column_name" or "nested.field").
48
- All columns must exist, if a a column doesn't exist the function will return an error.
48
+ All columns must exist, if a column doesn't exist the function will return an error.
49
49
  """
50
50
  self._core.drop_columns(column_paths)
51
51