[![PyPI](https://img.shields.io/pypi/v/scyllaft?style=for-the-badge)](https://pypi.org/project/scyllaft/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/scyllaft?style=for-the-badge)](https://pypistats.org/packages/scyllaft)
# Async Scylla driver for python
Python driver for ScyllaDB written in Rust. Though description says it's for scylla,
however it can be used with Cassandra and AWS keyspaces as well.
This driver uses official [ScyllaDB driver](https://github.com/scylladb/scylla-rust-driver) for [Rust](https://github.com/rust-lang/rust/) and exposes python API to interact with it.
This driver is a fork of Intreecom/scyllapy with additional features for pulling data.
## Installation
To install it, use your favorite package manager for python packages:
```bash
pip install scyllaft
```
Also, you can build from sources. To do it, install stable rust, [maturin](https://github.com/PyO3/maturin) and openssl libs.
```bash
maturin build --release --out dist
# Then install whl file from dist folder.
pip install dist/*
```
## Usage
The usage is pretty straitforward. Create a Scylla instance, run startup and start executing queries.
```python
import asyncio
from scyllaft import Scylla
async def main():
scylla = Scylla(["localhost:9042"], keyspace="keyspace")
await scylla.startup()
await scylla.execute("SELECT * FROM table")
await scylla.shutdown()
if __name__ == "__main__":
asyncio.run(main())
```
## Mapping of Scylla/Python Types
| Scylla type | Python type |
| ----------- | ---------------------- |
| int | int |
| tinyint | extra_types.TinyInt |
| bigint | extra_types.BigInt |
| varint | any int type |
| float | float |
| double | extra_types.Double |
| decimal | decimal.Decimal |
| ascii | str |
| text | str |
| varchar | str |
| blob | bytes |
| boolean | bool |
| counter | extra_types.Counter |
| date | datetime.date |
| uuid | uuid.UUID |
| inet | ipaddress |
| time | datetime.time |
| timestamp | datetime.datetime |
| duration | dateutil.relativedelta |
All types from `extra_types` module are used to eliminate any possible ambiguity while passing parameters to queries. You can find more information about them in `Extra types` section.
We use relative delta from `dateutil` for duration, because it's the only way to represent it in python. Since scylla operates with months, days and nanosecond, there's no way we can represent it in python, because months are variable length.
## Parametrizing queries
While executing queries sometimes you may want to fine-tune some parameters, or dynamically pass values to the query.
Passing parameters is simple. You need to add a paramters list to the query.
```python
await scylla.execute(
"INSERT INTO otps(id, otp) VALUES (?, ?)",
[uuid.uuid4(), uuid.uuid4().hex],
)
```
Queries can be modified further by using `Query` class. It allows you to define
consistency for query or enable tracing.
```python
from scyllaft import Scylla, Query, Consistency, SerialConsistency
async def make_query(scylla: Scylla) -> None:
query = Query(
"SELECT * FROM table",
consistency=Consistency.ALL,
serial_consistency=SerialConsistency.LOCAL_SERIAL,
request_timeout=1,
timestamp=int(time.time()),
is_idempotent=False,
tracing=True,
)
result = await scylla.execute(query)
print(result.all())
```
Also, with queries you can tweak random parameters for a specific execution.
```python
query = Query("SELECT * FROM table")
new_query = query.with_consistency(Consistency.ALL)
```
All `with_` methods create new query, copying all other parameters.
## Named parameters
Also, you can provide named parameters to querties, by using name
placeholders instead of `?`.
For example:
```python
async def insert(scylla: Scylla):
await scylla.execute(
"INSERT INTO table(id, name) VALUES (:id, :name)",
params={"id": uuid.uuid4(), "name": uuid.uuid4().hex}
)
```
Important note: All variables should be in snake_case.
Otherwise the error may be raised or parameter may not be placed in query correctly.
This happens, because scylla makes all parameters in query lowercase.
The scyllaft makes all parameters lowercase, but you may run into problems,
if you use multiple parameters that differ only in cases of some letters.
## Preparing queries
Also, queries can be prepared. You can either prepare raw strings, or `Query` objects.
```python
from scyllaft import Scylla, Query, PreparedQuery
async def prepare(scylla: Scylla, query: str | Query) -> PreparedQuery:
return await scylla.prepare(query)
```
You can execute prepared queries by passing them to `execute` method.
```python
async def run_prepared(scylla: Scylla) -> None:
prepared = await scylla.prepare("INSERT INTO memse(title) VALUES (?)")
await scylla.execute(prepared, ("American joke",))
```
### Batching
We support batches. Batching can help a lot when you have lots of queries that you want to execute at the same time.
```python
from scyllaft import Scylla, Batch
async def run_batch(scylla: Scylla, num_queries: int) -> None:
batch = Batch()
for _ in range(num_queries):
batch.add_query("SELECT * FROM table WHERE id = ?")
await scylla.batch(batch, [(i,) for i in range(num_queries)])
```
Here we pass query as strings. But you can also add Prepared statements or Query objects.
Also, note that we pass list of lists as parametes for execute. Each element of
the list is going to be used in the query with the same index. But named parameters
are not supported for batches.
```python
async def run_batch(scylla: Scylla, num_queries: int) -> None:
batch = Batch()
batch.add_query("SELECT * FROM table WHERE id = :id")
await scylla.batch(batch, [{"id": 1}]) # Will rase an error!
```
## Pagination
Sometimes you want to query lots of data. For such cases it's better not to
fetch all results at once, but fetch them using pagination. It reduces load
not only on your application, but also on a cluster.
To execute query with pagination, simply add `paged=True` in execute method.
After doing so, `execute` method will return `IterableQueryResult`, instead of `QueryResult`.
Instances of `IterableQueryResult` can be iterated with `async for` statements.
You, as a client, won't see any information about pages, it's all handeled internally within a driver.
Please note, that paginated queries are slower to fetch all rows, but much more memory efficent for large datasets.
```python
result = await scylla.execute("SELECT * FROM table", paged=True)
async for row in result:
print(row)
```
Of course, you can change how results returned to you, by either using `scalars` or
`as_cls`. For example:
```python
async def func(scylla: Scylla) -> None:
rows = await scylla.execute("SELECT id FROM table", paged=True)
# Will print ids of each returned row.
async for test_id in rows.scalars():
print(test_id)
```
```python
from dataclasses import dataclass
@dataclass
class MyDTO:
id: int
val: int
async def func(scylla: Scylla) -> None:
rows = await scylla.execute("SELECT * FROM table", paged=True)
# Will print ids of each returned row.
async for my_dto in rows.as_cls(MyDTO):
print(my_dto.id, my_dto.val)
```
## Execution profiles
You can define profiles using `ExecutionProfile` class. After that the
profile can be used while creating a cluster or when defining queries.
```python
from scyllaft import Consistency, ExecutionProfile, Query, Scylla, SerialConsistency
from scyllaft.load_balancing import LoadBalancingPolicy, LatencyAwareness
default_profile = ExecutionProfile(
consistency=Consistency.LOCAL_QUORUM,
serial_consistency=SerialConsistency.LOCAL_SERIAL,
request_timeout=2,
)
async def main():
query_profile = ExecutionProfile(
consistency=Consistency.ALL,
serial_consistency=SerialConsistency.SERIAL,
# Load balancing cannot be constructed without running event loop.
# If you won't do it inside async funcion, it will result in error.
load_balancing_policy=await LoadBalancingPolicy.build(
token_aware=True,
prefer_rack="rack1",
prefer_datacenter="dc1",
permit_dc_failover=True,
shuffling_replicas=True,
latency_awareness=LatencyAwareness(
minimum_measurements=10,
retry_period=1000,
exclusion_threshold=1.4,
update_rate=1000,
scale=2,
),
),
)
scylla = Scylla(
["192.168.32.4"],
default_execution_profile=default_profile,
)
await scylla.startup()
await scylla.execute(
Query(
"SELECT * FROM system_schema.keyspaces;",
profile=query_profile,
)
)
```
### Results
Every query returns a class that represents returned rows. It allows you to not fetch
and parse actual data if you don't need it. **Please be aware** that if your query was
not expecting any rows in return. Like for `Update` or `Insert` queries. The `RuntimeError` is raised when you call `all` or `first`.
```python
result = await scylla.execute("SELECT * FROM table")
print(result.all())
```
If you were executing query with tracing, you can get tracing id from results.
```python
result = await scylla.execute(Query("SELECT * FROM table", tracing=True))
print(result.trace_id)
```
Also it's possible to parse your data using custom classes. You
can use dataclasses or Pydantic.
```python
from dataclasses import dataclass
@dataclass
class MyDTO:
id: uuid.UUID
name: str
result = await scylla.execute("SELECT * FROM inbox")
print(result.all(as_class=MyDTO))
```
Or with pydantic.
```python
from pydantic import BaseModel
class MyDTO(BaseModel):
user_id: uuid.UUID
chat_id: uuid.UUID
result = await scylla.execute("SELECT * FROM inbox")
print(result.all(as_class=MyDTO))
```
## Extra types
Since Rust enforces typing, it's hard to identify which value
user tries to pass as a parameter. For example, `1` that comes from python
can be either `tinyint`, `smallint` or even `bigint`. But we cannot say for sure
how many bytes should we send to server. That's why we created some extra_types to
eliminate any possible ambigousnity.
You can find these types in `extra_types` module from scyllaft.
```python
from scyllaft import Scylla, extra_types
async def execute(scylla: Scylla) -> None:
await scylla.execute(
"INSERT INTO table(id, name) VALUES (?, ?)",
[extra_types.BigInt(1), "memelord"],
)
```
## User defined types
We also support user defined types. You can pass them as a parameter to query.
Or parse it as a model in response.
Here's binding example. Imagine we have defined a type in scylla like this:
```cql
CREATE TYPE IF NOT EXISTS test (
id int,
name text
);
```
Now we need to define a model for it in python.
```python
from dataclasses import dataclass
from scyllaft.extra_types import ScyllaPyUDT
@dataclass
class TestUDT(ScyllaPyUDT):
# Always define fields in the same order as in scylla.
# Otherwise you will get an error, or wrong data.
id: int
name: str
async def execute(scylla: Scylla) -> None:
await scylla.execute(
"INSERT INTO table(id, udt_col) VALUES (?, ?)",
[1, TestUDT(id=1, name="test")],
)
```
We also support pydantic based models. Decalre them like this:
```python
from pydantic import BaseModel
from scyllaft.extra_types import ScyllaPyUDT
class TestUDT(BaseModel, ScyllaPyUDT):
# Always define fields in the same order as in scylla.
# Otherwise you will get an error, or wrong data.
id: int
name: str
```
# Query building
Scyllaft gives you ability to build queries,
instead of working with raw cql. The main advantage that it's harder to make syntax error,
while creating queries.
Base classes for Query building can be found in `scyllaft.query_builder`.
Usage example:
```python
from scyllaft import Scylla
from scyllaft.query_builder import Insert, Select, Update, Delete
async def main(scylla: Scylla):
await scylla.execute("CREATE TABLE users(id INT PRIMARY KEY, name TEXT)")
user_id = 1
# We create a user with id and name.
await Insert("users").set("id", user_id).set(
"name", "user"
).if_not_exists().execute(scylla)
# We update it's name to be user2
await Update("users").set("name", "user2").where("id = ?", [user_id]).execute(
scylla
)
# We select all users with id = user_id;
res = await Select("users").where("id = ?", [user_id]).execute(scylla)
# Verify that it's correct.
assert res.first() == {"id": 1, "name": "user2"}
# We delete our user.
await Delete("users").where("id = ?", [user_id]).if_exists().execute(scylla)
res = await Select("users").where("id = ?", [user_id]).execute(scylla)
# Verify that user is deleted.
assert not res.all()
await scylla.execute("DROP TABLE users")
```
Also, you can pass built queries into InlineBatches. You cannot use queries built with query_builder module with default batches. This constraint is exists, because we
need to use values from within your queries and should ignore all parameters passed in
`batch` method of scylla.
Here's batch usage example.
```python
from scyllaft import Scylla, InlineBatch
from scyllaft.query_builder import Insert
async def execute_batch(scylla: Scylla) -> None:
batch = InlineBatch()
for i in range(10):
Insert("users").set("id", i).set("name", "test").add_to_batch(batch)
await scylla.batch(batch)
```
## Paging
Queries that were built with QueryBuilder also support paged returns.
But it supported only for select, because update, delete and insert should
not return anything and it makes no sense implementing it.
To make built `Select` query return paginated iterator, add paged parameter in execute method.
The `paged` argument represents the page_size of the query.
```python
rows = await Select("test").execute(scylla, paged=5000)
async for row in rows:
print(row['id'])
```
Raw data
{
"_id": null,
"home_page": null,
"name": "scyllaft",
"maintainer": null,
"docs_url": null,
"requires_python": "<4,>=3.8",
"maintainer_email": null,
"keywords": "scylla, cassandra, async-driver, scylla-driver",
"author": null,
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/52/43/b5ff17925d6f547c2e6db5749cee09d41c4b217d4c668ae03d85e09703d7/scyllaft-1.6.3.tar.gz",
"platform": null,
"description": "[![PyPI](https://img.shields.io/pypi/v/scyllaft?style=for-the-badge)](https://pypi.org/project/scyllaft/)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/scyllaft?style=for-the-badge)](https://pypistats.org/packages/scyllaft)\n\n\n# Async Scylla driver for python\n\nPython driver for ScyllaDB written in Rust. Though description says it's for scylla,\nhowever it can be used with Cassandra and AWS keyspaces as well.\n\nThis driver uses official [ScyllaDB driver](https://github.com/scylladb/scylla-rust-driver) for [Rust](https://github.com/rust-lang/rust/) and exposes python API to interact with it.\n\nThis driver is a fork of Intreecom/scyllapy with additional features for pulling data.\n\n## Installation\n\nTo install it, use your favorite package manager for python packages:\n\n```bash\npip install scyllaft\n```\n\nAlso, you can build from sources. To do it, install stable rust, [maturin](https://github.com/PyO3/maturin) and openssl libs.\n\n```bash\nmaturin build --release --out dist\n# Then install whl file from dist folder.\npip install dist/*\n```\n\n## Usage\n\nThe usage is pretty straitforward. Create a Scylla instance, run startup and start executing queries.\n\n```python\nimport asyncio\n\nfrom scyllaft import Scylla\n\n\nasync def main():\n scylla = Scylla([\"localhost:9042\"], keyspace=\"keyspace\")\n await scylla.startup()\n await scylla.execute(\"SELECT * FROM table\")\n await scylla.shutdown()\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n\n```\n\n## Mapping of Scylla/Python Types\n\n| Scylla type | Python type |\n| ----------- | ---------------------- |\n| int | int |\n| tinyint | extra_types.TinyInt |\n| bigint | extra_types.BigInt |\n| varint | any int type |\n| float | float |\n| double | extra_types.Double |\n| decimal | decimal.Decimal |\n| ascii | str |\n| text | str |\n| varchar | str |\n| blob | bytes |\n| boolean | bool |\n| counter | extra_types.Counter |\n| date | datetime.date |\n| uuid | uuid.UUID |\n| inet | ipaddress |\n| time | datetime.time |\n| timestamp | datetime.datetime |\n| duration | dateutil.relativedelta |\n\nAll types from `extra_types` module are used to eliminate any possible ambiguity while passing parameters to queries. You can find more information about them in `Extra types` section.\n\nWe use relative delta from `dateutil` for duration, because it's the only way to represent it in python. Since scylla operates with months, days and nanosecond, there's no way we can represent it in python, because months are variable length.\n\n## Parametrizing queries\n\nWhile executing queries sometimes you may want to fine-tune some parameters, or dynamically pass values to the query.\n\nPassing parameters is simple. You need to add a paramters list to the query.\n\n```python\n await scylla.execute(\n \"INSERT INTO otps(id, otp) VALUES (?, ?)\",\n [uuid.uuid4(), uuid.uuid4().hex],\n )\n```\n\nQueries can be modified further by using `Query` class. It allows you to define\nconsistency for query or enable tracing.\n\n```python\nfrom scyllaft import Scylla, Query, Consistency, SerialConsistency\n\nasync def make_query(scylla: Scylla) -> None:\n query = Query(\n \"SELECT * FROM table\",\n consistency=Consistency.ALL,\n serial_consistency=SerialConsistency.LOCAL_SERIAL,\n request_timeout=1,\n timestamp=int(time.time()),\n is_idempotent=False,\n tracing=True,\n )\n result = await scylla.execute(query)\n print(result.all())\n```\n\nAlso, with queries you can tweak random parameters for a specific execution.\n\n```python\nquery = Query(\"SELECT * FROM table\")\n\nnew_query = query.with_consistency(Consistency.ALL)\n```\n\nAll `with_` methods create new query, copying all other parameters.\n\n## Named parameters\n\nAlso, you can provide named parameters to querties, by using name\nplaceholders instead of `?`.\n\nFor example:\n\n```python\nasync def insert(scylla: Scylla):\n await scylla.execute(\n \"INSERT INTO table(id, name) VALUES (:id, :name)\",\n params={\"id\": uuid.uuid4(), \"name\": uuid.uuid4().hex}\n )\n```\n\nImportant note: All variables should be in snake_case.\nOtherwise the error may be raised or parameter may not be placed in query correctly.\nThis happens, because scylla makes all parameters in query lowercase.\n\nThe scyllaft makes all parameters lowercase, but you may run into problems,\nif you use multiple parameters that differ only in cases of some letters.\n\n\n## Preparing queries\n\nAlso, queries can be prepared. You can either prepare raw strings, or `Query` objects.\n\n```python\nfrom scyllaft import Scylla, Query, PreparedQuery\n\n\nasync def prepare(scylla: Scylla, query: str | Query) -> PreparedQuery:\n return await scylla.prepare(query)\n```\n\nYou can execute prepared queries by passing them to `execute` method.\n\n```python\nasync def run_prepared(scylla: Scylla) -> None:\n prepared = await scylla.prepare(\"INSERT INTO memse(title) VALUES (?)\")\n await scylla.execute(prepared, (\"American joke\",))\n```\n\n### Batching\n\nWe support batches. Batching can help a lot when you have lots of queries that you want to execute at the same time.\n\n```python\nfrom scyllaft import Scylla, Batch\n\n\nasync def run_batch(scylla: Scylla, num_queries: int) -> None:\n batch = Batch()\n for _ in range(num_queries):\n batch.add_query(\"SELECT * FROM table WHERE id = ?\")\n await scylla.batch(batch, [(i,) for i in range(num_queries)])\n```\n\nHere we pass query as strings. But you can also add Prepared statements or Query objects.\n\nAlso, note that we pass list of lists as parametes for execute. Each element of\nthe list is going to be used in the query with the same index. But named parameters\nare not supported for batches.\n\n```python\nasync def run_batch(scylla: Scylla, num_queries: int) -> None:\n batch = Batch()\n batch.add_query(\"SELECT * FROM table WHERE id = :id\")\n await scylla.batch(batch, [{\"id\": 1}]) # Will rase an error!\n```\n\n## Pagination\n\nSometimes you want to query lots of data. For such cases it's better not to\nfetch all results at once, but fetch them using pagination. It reduces load\nnot only on your application, but also on a cluster.\n\nTo execute query with pagination, simply add `paged=True` in execute method.\nAfter doing so, `execute` method will return `IterableQueryResult`, instead of `QueryResult`.\nInstances of `IterableQueryResult` can be iterated with `async for` statements.\nYou, as a client, won't see any information about pages, it's all handeled internally within a driver.\n\nPlease note, that paginated queries are slower to fetch all rows, but much more memory efficent for large datasets.\n\n```python\n result = await scylla.execute(\"SELECT * FROM table\", paged=True)\n async for row in result:\n print(row)\n\n```\n\nOf course, you can change how results returned to you, by either using `scalars` or\n`as_cls`. For example:\n\n```python\nasync def func(scylla: Scylla) -> None:\n rows = await scylla.execute(\"SELECT id FROM table\", paged=True)\n # Will print ids of each returned row.\n async for test_id in rows.scalars():\n print(test_id)\n\n```\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass MyDTO:\n id: int\n val: int\n\nasync def func(scylla: Scylla) -> None:\n rows = await scylla.execute(\"SELECT * FROM table\", paged=True)\n # Will print ids of each returned row.\n async for my_dto in rows.as_cls(MyDTO):\n print(my_dto.id, my_dto.val)\n\n```\n\n## Execution profiles\n\nYou can define profiles using `ExecutionProfile` class. After that the\nprofile can be used while creating a cluster or when defining queries.\n\n```python\nfrom scyllaft import Consistency, ExecutionProfile, Query, Scylla, SerialConsistency\nfrom scyllaft.load_balancing import LoadBalancingPolicy, LatencyAwareness\n\ndefault_profile = ExecutionProfile(\n consistency=Consistency.LOCAL_QUORUM,\n serial_consistency=SerialConsistency.LOCAL_SERIAL,\n request_timeout=2,\n)\n\nasync def main():\n query_profile = ExecutionProfile(\n consistency=Consistency.ALL,\n serial_consistency=SerialConsistency.SERIAL,\n # Load balancing cannot be constructed without running event loop.\n # If you won't do it inside async funcion, it will result in error.\n load_balancing_policy=await LoadBalancingPolicy.build(\n token_aware=True,\n prefer_rack=\"rack1\",\n prefer_datacenter=\"dc1\",\n permit_dc_failover=True,\n shuffling_replicas=True,\n latency_awareness=LatencyAwareness(\n minimum_measurements=10,\n retry_period=1000,\n exclusion_threshold=1.4,\n update_rate=1000,\n scale=2,\n ),\n ),\n )\n\n scylla = Scylla(\n [\"192.168.32.4\"],\n default_execution_profile=default_profile,\n )\n await scylla.startup()\n await scylla.execute(\n Query(\n \"SELECT * FROM system_schema.keyspaces;\",\n profile=query_profile,\n )\n )\n```\n\n### Results\n\nEvery query returns a class that represents returned rows. It allows you to not fetch\nand parse actual data if you don't need it. **Please be aware** that if your query was\nnot expecting any rows in return. Like for `Update` or `Insert` queries. The `RuntimeError` is raised when you call `all` or `first`.\n\n```python\nresult = await scylla.execute(\"SELECT * FROM table\")\nprint(result.all())\n```\n\nIf you were executing query with tracing, you can get tracing id from results.\n\n```python\nresult = await scylla.execute(Query(\"SELECT * FROM table\", tracing=True))\nprint(result.trace_id)\n```\n\nAlso it's possible to parse your data using custom classes. You\ncan use dataclasses or Pydantic.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass MyDTO:\n id: uuid.UUID\n name: str\n\nresult = await scylla.execute(\"SELECT * FROM inbox\")\nprint(result.all(as_class=MyDTO))\n```\n\nOr with pydantic.\n\n```python\nfrom pydantic import BaseModel\n\nclass MyDTO(BaseModel):\n user_id: uuid.UUID\n chat_id: uuid.UUID\n\nresult = await scylla.execute(\"SELECT * FROM inbox\")\nprint(result.all(as_class=MyDTO))\n```\n\n## Extra types\n\nSince Rust enforces typing, it's hard to identify which value\nuser tries to pass as a parameter. For example, `1` that comes from python\ncan be either `tinyint`, `smallint` or even `bigint`. But we cannot say for sure\nhow many bytes should we send to server. That's why we created some extra_types to\neliminate any possible ambigousnity.\n\nYou can find these types in `extra_types` module from scyllaft.\n\n```python\nfrom scyllaft import Scylla, extra_types\n\nasync def execute(scylla: Scylla) -> None:\n await scylla.execute(\n \"INSERT INTO table(id, name) VALUES (?, ?)\",\n [extra_types.BigInt(1), \"memelord\"],\n )\n```\n\n## User defined types\n\nWe also support user defined types. You can pass them as a parameter to query.\nOr parse it as a model in response.\n\nHere's binding example. Imagine we have defined a type in scylla like this:\n\n```cql\nCREATE TYPE IF NOT EXISTS test (\n id int,\n name text\n);\n```\n\nNow we need to define a model for it in python.\n\n```python\nfrom dataclasses import dataclass\nfrom scyllaft.extra_types import ScyllaPyUDT\n\n@dataclass\nclass TestUDT(ScyllaPyUDT):\n # Always define fields in the same order as in scylla.\n # Otherwise you will get an error, or wrong data.\n id: int\n name: str\n\nasync def execute(scylla: Scylla) -> None:\n await scylla.execute(\n \"INSERT INTO table(id, udt_col) VALUES (?, ?)\",\n [1, TestUDT(id=1, name=\"test\")],\n )\n\n```\n\nWe also support pydantic based models. Decalre them like this:\n\n```python\nfrom pydantic import BaseModel\nfrom scyllaft.extra_types import ScyllaPyUDT\n\n\nclass TestUDT(BaseModel, ScyllaPyUDT):\n # Always define fields in the same order as in scylla.\n # Otherwise you will get an error, or wrong data.\n id: int\n name: str\n\n```\n\n# Query building\n\nScyllaft gives you ability to build queries,\ninstead of working with raw cql. The main advantage that it's harder to make syntax error,\nwhile creating queries.\n\nBase classes for Query building can be found in `scyllaft.query_builder`.\n\nUsage example:\n\n```python\nfrom scyllaft import Scylla\nfrom scyllaft.query_builder import Insert, Select, Update, Delete\n\n\nasync def main(scylla: Scylla):\n await scylla.execute(\"CREATE TABLE users(id INT PRIMARY KEY, name TEXT)\")\n\n user_id = 1\n\n # We create a user with id and name.\n await Insert(\"users\").set(\"id\", user_id).set(\n \"name\", \"user\"\n ).if_not_exists().execute(scylla)\n\n # We update it's name to be user2\n await Update(\"users\").set(\"name\", \"user2\").where(\"id = ?\", [user_id]).execute(\n scylla\n )\n\n # We select all users with id = user_id;\n res = await Select(\"users\").where(\"id = ?\", [user_id]).execute(scylla)\n # Verify that it's correct.\n assert res.first() == {\"id\": 1, \"name\": \"user2\"}\n\n # We delete our user.\n await Delete(\"users\").where(\"id = ?\", [user_id]).if_exists().execute(scylla)\n\n res = await Select(\"users\").where(\"id = ?\", [user_id]).execute(scylla)\n\n # Verify that user is deleted.\n assert not res.all()\n\n await scylla.execute(\"DROP TABLE users\")\n\n```\n\nAlso, you can pass built queries into InlineBatches. You cannot use queries built with query_builder module with default batches. This constraint is exists, because we\nneed to use values from within your queries and should ignore all parameters passed in\n`batch` method of scylla.\n\nHere's batch usage example.\n\n```python\nfrom scyllaft import Scylla, InlineBatch\nfrom scyllaft.query_builder import Insert\n\n\nasync def execute_batch(scylla: Scylla) -> None:\n batch = InlineBatch()\n for i in range(10):\n Insert(\"users\").set(\"id\", i).set(\"name\", \"test\").add_to_batch(batch)\n await scylla.batch(batch)\n\n```\n\n## Paging\n\nQueries that were built with QueryBuilder also support paged returns.\nBut it supported only for select, because update, delete and insert should\nnot return anything and it makes no sense implementing it.\nTo make built `Select` query return paginated iterator, add paged parameter in execute method.\n\nThe `paged` argument represents the page_size of the query.\n\n```python\n rows = await Select(\"test\").execute(scylla, paged=5000)\n async for row in rows:\n print(row['id'])\n```\n\n",
"bugtrack_url": null,
"license": null,
"summary": "Async scylla driver for python - FentechFork",
"version": "1.6.3",
"project_urls": null,
"split_keywords": [
"scylla",
" cassandra",
" async-driver",
" scylla-driver"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "3e32346bc34826c2834d20705addd47ec328914c5b77b8eecb1919c7aeeb8be3",
"md5": "553a4f3eccf21abe6f5e6e69b6e49fb4",
"sha256": "fddc308c6afcc0d8a3d0a3583860147bdaecb38fd2945912eb268f17fce6f50b"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-macosx_10_12_x86_64.whl",
"has_sig": false,
"md5_digest": "553a4f3eccf21abe6f5e6e69b6e49fb4",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 2951948,
"upload_time": "2024-08-11T07:46:55",
"upload_time_iso_8601": "2024-08-11T07:46:55.583978Z",
"url": "https://files.pythonhosted.org/packages/3e/32/346bc34826c2834d20705addd47ec328914c5b77b8eecb1919c7aeeb8be3/scyllaft-1.6.3-cp38-abi3-macosx_10_12_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "64b1f1b41f6dfb42002722b48255a4abaf3c3c82212f3c3f5a263f45ed0cb27e",
"md5": "ec3346b646a3530de9c15eac1ceba35f",
"sha256": "269e6773636afa5882cbbf975223115452160c0fb7f432f4e309e3f955932243"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-macosx_11_0_arm64.whl",
"has_sig": false,
"md5_digest": "ec3346b646a3530de9c15eac1ceba35f",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 3166193,
"upload_time": "2024-08-11T07:46:58",
"upload_time_iso_8601": "2024-08-11T07:46:58.330468Z",
"url": "https://files.pythonhosted.org/packages/64/b1/f1b41f6dfb42002722b48255a4abaf3c3c82212f3c3f5a263f45ed0cb27e/scyllaft-1.6.3-cp38-abi3-macosx_11_0_arm64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7380b55a7c6a38683ddd6f600f74c925168e8f98f6e4597da229f69434cea453",
"md5": "cf2dd61949e215d7da782282a22885be",
"sha256": "f04109a506e6a132a2ef8b31cb95cf193f35fbeb325829d09fec691f335a67be"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
"has_sig": false,
"md5_digest": "cf2dd61949e215d7da782282a22885be",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 3497004,
"upload_time": "2024-08-11T07:47:00",
"upload_time_iso_8601": "2024-08-11T07:47:00.780468Z",
"url": "https://files.pythonhosted.org/packages/73/80/b55a7c6a38683ddd6f600f74c925168e8f98f6e4597da229f69434cea453/scyllaft-1.6.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "468a24244d939bfb5192634d7264d19371c33df069edff16eb9f3f3db03425a7",
"md5": "46b875ef1d9e97de5b781327f52676cd",
"sha256": "940d737e96e8f7f2aca6895a60f0cad10c0c812b83d412b5b14cc41f3440ad9b"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
"has_sig": false,
"md5_digest": "46b875ef1d9e97de5b781327f52676cd",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 2959732,
"upload_time": "2024-08-11T07:47:03",
"upload_time_iso_8601": "2024-08-11T07:47:03.090066Z",
"url": "https://files.pythonhosted.org/packages/46/8a/24244d939bfb5192634d7264d19371c33df069edff16eb9f3f3db03425a7/scyllaft-1.6.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "61699ef933046b3b24af5759185554686bea962ef828dcabef17fec1fcfaabf2",
"md5": "a4f61c54aba559f2a1985b2499c502ef",
"sha256": "923e6c72f7866b3d49b9476e6a27efb99850bc42461231893dfca43f56fb75df"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl",
"has_sig": false,
"md5_digest": "a4f61c54aba559f2a1985b2499c502ef",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 3279082,
"upload_time": "2024-08-11T07:47:05",
"upload_time_iso_8601": "2024-08-11T07:47:05.568758Z",
"url": "https://files.pythonhosted.org/packages/61/69/9ef933046b3b24af5759185554686bea962ef828dcabef17fec1fcfaabf2/scyllaft-1.6.3-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "64fb6f75a36cb4454d89fc948651cca6c4bfce9cec3680431b153b49758536cd",
"md5": "b07743d2060db64b2298bda604fe7ad8",
"sha256": "8927ca78209dfe8f72d2f0b5c6cc54da4f723e83ed9bbf621a05ae2c17c04406"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
"has_sig": false,
"md5_digest": "b07743d2060db64b2298bda604fe7ad8",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 3173360,
"upload_time": "2024-08-11T07:47:08",
"upload_time_iso_8601": "2024-08-11T07:47:08.366516Z",
"url": "https://files.pythonhosted.org/packages/64/fb/6f75a36cb4454d89fc948651cca6c4bfce9cec3680431b153b49758536cd/scyllaft-1.6.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7c4a63b3119e1d5a09047237a4de6a867bfe99d53ca8f7d67d9f2b4783c26b6f",
"md5": "29b97b04e0d45c699b9eb045c6c4a9ef",
"sha256": "75f6bd6a94b6e7c0a1a8f8a7213f8dad6440976f4f91a2d494d77d01873b48d5"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "29b97b04e0d45c699b9eb045c6c4a9ef",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 3342974,
"upload_time": "2024-08-11T07:47:10",
"upload_time_iso_8601": "2024-08-11T07:47:10.918695Z",
"url": "https://files.pythonhosted.org/packages/7c/4a/63b3119e1d5a09047237a4de6a867bfe99d53ca8f7d67d9f2b4783c26b6f/scyllaft-1.6.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "025eb33105d5f80ae3d04a074c8fef588ebcdd9f435380ee29ad55485bc6a285",
"md5": "9f8c27f786127062e36a1432e1b06e0b",
"sha256": "001b3eb9db60a40305d27f47ebd47f39eadd396fd18c270521eba4e701051de6"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-musllinux_1_2_i686.whl",
"has_sig": false,
"md5_digest": "9f8c27f786127062e36a1432e1b06e0b",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 3397873,
"upload_time": "2024-08-11T07:47:13",
"upload_time_iso_8601": "2024-08-11T07:47:13.627112Z",
"url": "https://files.pythonhosted.org/packages/02/5e/b33105d5f80ae3d04a074c8fef588ebcdd9f435380ee29ad55485bc6a285/scyllaft-1.6.3-cp38-abi3-musllinux_1_2_i686.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "2eb264f329f4790614e789b2ed41b52b158dd30df5a6512980d95eea4130426d",
"md5": "783e7b3823a8ac4569e457720fe08982",
"sha256": "e0334b74a372d30abdfc8897aeef9a43d01177af880c6bf61115f6cc6ea79fc0"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-musllinux_1_2_x86_64.whl",
"has_sig": false,
"md5_digest": "783e7b3823a8ac4569e457720fe08982",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 3481322,
"upload_time": "2024-08-11T07:47:15",
"upload_time_iso_8601": "2024-08-11T07:47:15.500987Z",
"url": "https://files.pythonhosted.org/packages/2e/b2/64f329f4790614e789b2ed41b52b158dd30df5a6512980d95eea4130426d/scyllaft-1.6.3-cp38-abi3-musllinux_1_2_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "eb5c6faa0428ff3fad4c23d503d8aea2ca937384ef8713912a3bc1affe482a4d",
"md5": "2174418c3a5a53c3cea4479d8c4d198f",
"sha256": "2b9638c04aba3598aeb8a7560d87237dc19326b5cc0946f8cbfc669a17c12bb3"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-win32.whl",
"has_sig": false,
"md5_digest": "2174418c3a5a53c3cea4479d8c4d198f",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 2287389,
"upload_time": "2024-08-11T07:47:17",
"upload_time_iso_8601": "2024-08-11T07:47:17.773634Z",
"url": "https://files.pythonhosted.org/packages/eb/5c/6faa0428ff3fad4c23d503d8aea2ca937384ef8713912a3bc1affe482a4d/scyllaft-1.6.3-cp38-abi3-win32.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "6dbf8efc3ef541b84ca89df7e4aa101c65221e5793afe7ca9e751cd2f77e172d",
"md5": "9fbfd497892144df97c00e341e964e1a",
"sha256": "c8738885f90770a162e3bd3a53c4e74283becd5e96f01761c18dfe1e1d5962ba"
},
"downloads": -1,
"filename": "scyllaft-1.6.3-cp38-abi3-win_amd64.whl",
"has_sig": false,
"md5_digest": "9fbfd497892144df97c00e341e964e1a",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": "<4,>=3.8",
"size": 2543612,
"upload_time": "2024-08-11T07:47:20",
"upload_time_iso_8601": "2024-08-11T07:47:20.041869Z",
"url": "https://files.pythonhosted.org/packages/6d/bf/8efc3ef541b84ca89df7e4aa101c65221e5793afe7ca9e751cd2f77e172d/scyllaft-1.6.3-cp38-abi3-win_amd64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "5243b5ff17925d6f547c2e6db5749cee09d41c4b217d4c668ae03d85e09703d7",
"md5": "52a2e7ba6af4a5a0c030dc2b8dadbd75",
"sha256": "2a84bc4170540e6c2a2148be7993a38cac831baeef992d9a827e8e2c4669c234"
},
"downloads": -1,
"filename": "scyllaft-1.6.3.tar.gz",
"has_sig": false,
"md5_digest": "52a2e7ba6af4a5a0c030dc2b8dadbd75",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4,>=3.8",
"size": 59120,
"upload_time": "2024-08-11T07:47:22",
"upload_time_iso_8601": "2024-08-11T07:47:22.156234Z",
"url": "https://files.pythonhosted.org/packages/52/43/b5ff17925d6f547c2e6db5749cee09d41c4b217d4c668ae03d85e09703d7/scyllaft-1.6.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-11 07:47:22",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "scyllaft"
}