{"id":19704653,"url":"https://github.com/datastax/astrapy","last_synced_at":"2025-10-04T14:05:37.393Z","repository":{"id":42450106,"uuid":"362473446","full_name":"datastax/astrapy","owner":"datastax","description":"AstraPy is a Pythonic interface for DataStax Astra DB and the Data API","archived":false,"fork":false,"pushed_at":"2025-10-02T10:17:33.000Z","size":5232,"stargazers_count":34,"open_issues_count":4,"forks_count":31,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-10-02T11:39:46.346Z","etag":null,"topics":["astradb","datastax","python","stargate"],"latest_commit_sha":null,"homepage":"https://github.com/datastax/astrapy","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/datastax.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-04-28T13:15:54.000Z","updated_at":"2025-09-29T08:59:27.000Z","dependencies_parsed_at":"2024-01-22T15:33:02.677Z","dependency_job_id":"faad2f49-cf0a-491e-9cbb-f67ef3087530","html_url":"https://github.com/datastax/astrapy","commit_stats":{"total_commits":27,"total_committers":2,"mean_commits":13.5,"dds":0.03703703703703709,"last_synced_commit":"b883c5e2782b5ea59734d0fa6c74fb5388ff6826"},"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"purl":"pkg:github/datastax/astrapy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastax%2Fastrapy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastax%2Fastrapy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastax%2Fastrapy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastax%2Fastrapy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datastax","download_url":"https://codeload.github.com/datastax/astrapy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastax%2Fastrapy/sbom","scorecard":{"id":324806,"data":{"date":"2025-08-11","repo":{"name":"github.com/datastax/astrapy","commit":"ed93c14ccd102f8780b85fe3a40667ee78aec3f7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.8,"checks":[{"name":"Maintained","score":10,"reason":"20 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/lint.yml:1","Warn: no topLevel permission defined: .github/workflows/local.yaml:1","Warn: no topLevel permission defined: .github/workflows/main.yml:1","Warn: no topLevel permission defined: .github/workflows/unit.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/lint.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/lint.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/local.yaml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/local.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/local.yaml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/local.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/local.yaml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/local.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/local.yaml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/local.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/unit.yaml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/unit.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/unit.yaml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/datastax/astrapy/unit.yaml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/lint.yml:24","Warn: pipCommand not pinned by hash: .github/workflows/local.yaml:33","Warn: pipCommand not pinned by hash: .github/workflows/main.yml:33","Warn: pipCommand not pinned by hash: .github/workflows/unit.yaml:40","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned","Info:   0 out of   4 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T02:11:26.742Z","repository_id":42450106,"created_at":"2025-08-18T02:11:26.742Z","updated_at":"2025-08-18T02:11:26.742Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278322146,"owners_count":25967874,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astradb","datastax","python","stargate"],"created_at":"2024-11-11T21:23:49.933Z","updated_at":"2025-10-04T14:05:37.385Z","avatar_url":"https://github.com/datastax.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# AstraPy\n\nA pythonic client for [DataStax Astra DB](https://astra.datastax.com).\n\n_This README targets **AstraPy version 2.0+**. Click [here](https://github.com/datastax/astrapy/blob/4601c5fa749925d961de1f114ca27690d1a71b13/README.md) for v1 and [here](https://github.com/datastax/astrapy/blob/cd3f5ce8146093e10a095709c0f5c3f8e3f2c7da/README.md) for the v0 API (which you should not really be using by now)._\n\n\n## Quickstart\n\nInstall with `pip install astrapy`.\n\nGet the *API Endpoint* and the *Token* to your Astra DB instance at [astra.datastax.com](https://astra.datastax.com).\n\nTry the following code after replacing the connection parameters:\n\n```python\nfrom astrapy import DataAPIClient\nfrom astrapy.constants import VectorMetric\nfrom astrapy.ids import UUID\nfrom astrapy.info import CollectionDefinition\n\n\nASTRA_DB_APPLICATION_TOKEN = \"AstraCS:...\"\nASTRA_DB_API_ENDPOINT = \"https://01234567-....apps.astra.datastax.com\"\n\n# Connect and create the Database object\nmy_client = DataAPIClient()\nmy_database = my_client.get_database(\n    ASTRA_DB_API_ENDPOINT,\n    token=ASTRA_DB_APPLICATION_TOKEN,\n)\n\n# Create a vector collection\nmy_collection = my_database.create_collection(\n    \"dreams_collection\",\n    definition=(\n        CollectionDefinition.builder()\n        .set_vector_dimension(3)\n        .set_vector_metric(VectorMetric.COSINE)\n        .build()\n    )\n)\n\n# Populate the collection with some documents\nmy_collection.insert_many(\n    [\n        {\n            \"_id\": UUID(\"018e65c9-e33d-749b-9386-e848739582f0\"),\n            \"summary\": \"Riding the waves\",\n            \"tags\": [\"sport\"],\n            \"$vector\": [0, 0.2, 1],\n        },\n        {\n            \"summary\": \"Friendly aliens in town\",\n            \"tags\": [\"scifi\"],\n            \"$vector\": [-0.3, 0, 0.8],\n        },\n        {\n            \"summary\": \"Meeting Beethoven at the dentist\",\n            \"$vector\": [0.2, 0.6, 0],\n        },\n    ],\n)\n\nmy_collection.update_one(\n    {\"tags\": \"sport\"},\n    {\"$set\": {\"summary\": \"Surfers' paradise\"}},\n)\n\n# Run a vector search\ncursor = my_collection.find(\n    {},\n    sort={\"$vector\": [0, 0.2, 0.4]},\n    limit=2,\n    include_similarity=True,\n)\n\nfor result in cursor:\n    print(f\"{result['summary']}: {result['$similarity']}\")\n\n# This would print:\n#   Surfers' paradise: 0.98238194\n#   Friendly aliens in town: 0.91873914\n\n# Resource cleanup\nmy_collection.drop()\n```\n\nNext steps:\n\n- More info and usage patterns are given in the docstrings of classes and methods\n- [Data API reference](https://docs.datastax.com/en/astra-db-serverless/api-reference/overview.html)\n- [AstraPy reference](https://docs.datastax.com/en/astra-api-docs/_attachments/python-client/astrapy/index.html)\n- Package on [PyPI](https://pypi.org/project/astrapy/)\n\n### Server-side embeddings\n\nAstraPy works with the \"vectorize\" feature of the Data API. This means that one can define server-side computation for vector embeddings and use text strings in place of a document vector, both in writing and in reading.\nThe transformation of said text into an embedding is handled by the Data API, using a provider and model you specify.\n\n```python\nmy_collection = database.create_collection(\n    \"my_vectorize_collection\",\n    definition=(\n        CollectionDefinition.builder()\n        .set_vector_service(\n            provider=\"example_vendor\",\n            model_name=\"embedding_model_name\",\n            authentication={\"providerKey\": \"\u003cSTORED_API_KEY_NAME\u003e\"}  # if needed\n        )\n        .build()\n    )\n)\n\nmy_collection.insert_one({\"$vectorize\": \"text to make into embedding\"})\n\ndocuments = my_collection.find(sort={\"$vectorize\": \"vector search query text\"})\n```\n\nSee the [Data API reference](https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html)\nfor more on this topic.\n\n### Hybrid search\n\nAstraPy supports the \"findAndRerank\" Data API command,\nwhich performs a hybrid search by combining results from a lexical search\nand a vector-based search in a single operation.\n\n```python\nr_results = my_collection.find_and_rerank(\n    sort={\"$hybrid\": \"query text\"},\n    limit=10,\n    include_scores=True,\n)\n\nfor r_result in r_results:\n    print(r_result.document, r_results.scores)\n```\n\nThis command will execute if the collection was created with the required settings\n(they are enabled by default).\n\nSee the Data API reference, and the docstring for the `find_and_rerank` method,\nfor more on this topic.\n\n### Using Tables\n\nThe example above uses a _collection_, where schemaless \"documents\" can be stored and retrieved.\nHere is an equivalent code that uses Tables, i.e. uniform, structured data where each _row_ has the\nsame _columns_, which are of a specific type:\n\n```python\nfrom astrapy import DataAPIClient\nfrom astrapy.constants import VectorMetric\nfrom astrapy.data_types import DataAPIVector\nfrom astrapy.info import (\n    CreateTableDefinition,\n    ColumnType,\n    TableVectorIndexDefinition,\n    TableVectorIndexOptions,\n)\n\n\nASTRA_DB_APPLICATION_TOKEN = \"AstraCS:...\"\nASTRA_DB_API_ENDPOINT = \"https://01234567-....apps.astra.datastax.com\"\n\n# Connect and create the Database object\nmy_client = DataAPIClient()\nmy_database = my_client.get_database(\n    ASTRA_DB_API_ENDPOINT,\n    token=ASTRA_DB_APPLICATION_TOKEN,\n)\n\n# Create a table and a vector index on it\ntable_definition = (\n    CreateTableDefinition.builder()\n    .add_column(\"dream_id\", ColumnType.INT)\n    .add_column(\"summary\", ColumnType.TEXT)\n    .add_set_column(\"tags\", ColumnType.TEXT)\n    .add_vector_column(\"dream_vector\", dimension=3)\n    .add_partition_by([\"dream_id\"])\n    .build()\n)\nindex_options=TableVectorIndexOptions(\n    metric=VectorMetric.COSINE,\n)\nmy_table = my_database.create_table(\"dreams_table\", definition=table_definition, if_not_exists=True)\nmy_table.create_vector_index(\"dreams_table_vec_idx\", column=\"dream_vector\", options=index_options, if_not_exists=True)\n\n# Populate the table with some rows\nmy_table.insert_many(\n    [\n        {\n            \"dream_id\": 103,\n            \"summary\": \"Riding the waves\",\n            \"tags\": [\"sport\"],\n            \"dream_vector\": DataAPIVector([0, 0.2, 1]),\n        },\n        {\n            \"dream_id\": 119,\n            \"summary\": \"Friendly aliens in town\",\n            \"tags\": [\"scifi\"],\n            \"dream_vector\": DataAPIVector([-0.3, 0, 0.8]),\n        },\n        {\n            \"dream_id\": 37,\n            \"summary\": \"Meeting Beethoven at the dentist\",\n            \"dream_vector\": DataAPIVector([0.2, 0.6, 0]),\n        },\n    ],\n)\n\nmy_table.update_one(\n    {\"dream_id\": 103},\n    {\"$set\": {\"summary\": \"Surfers' paradise\"}},\n)\n\n# Run a vector search\ncursor = my_table.find(\n    {},\n    sort={\"dream_vector\": DataAPIVector([0, 0.2, 0.4])},\n    limit=2,\n    include_similarity=True,\n)\n\nfor result in cursor:\n    print(f\"{result['summary']}: {result['$similarity']}\")\n\n# This would print:\n#   Surfers' paradise: 0.98238194\n#   Friendly aliens in town: 0.91873914\n\n# Resource cleanup\nmy_table.drop()\n```\n\nFor more on Tables, consult the [Data API documentation about Tables](https://docs.datastax.com/en/astra-db-serverless/api-reference/tables.html). Note that most features of Collections, with due modifications, hold for Tables as well (e.g. \"vectorize\", i.e. server-side embeddings).\n\n#### Maps as association lists\n\nWhen working with Tables, `map` columns with key of a type other than text\nmust be expressed as association lists,\ni.e. nested lists of lists: `[[key1, value1], [key2, value2], ...]`, in payloads.\n\nBy default, `Table` objects in AstraPy will use the association-list representation\nonly for maps wrapped by a `DataAPIMap`, and leave regular Python `dict` objects\nunchanged in payloads: in practice the following row would be sent to the API as shown:\n\n```python\nfrom astrapy.data_types import DataAPIMap\n\nrow_to_insert = {\n    \"map_1\": {\"k\": \"v\"},\n    \"map_2\": DataAPIMap({\"k1\": \"v1\", \"k2\": \"v2\"}),\n}\n\n# Will be serialized as JSON like this:\n#    {\n#        \"map_1\": {\"k\": \"v\"},\n#        \"map_2\": [[\"k1\", \"v1\"], [\"k2\", \"v2\"]]\n#    }\n```\n\nThis behaviour is controlled by the API Option `serdes_options.encode_maps_as_lists_in_tables`,\nwhich defaults to `MapEncodingMode.DATAAPIMAPS`. Other options are:\n\n- `MapEncodingMode.ALWAYS` to convert also regular Python `dict` values into lists of pairs;\n- `MapEncodingMode.NEVER` to never apply such conversion.\n\nThe following code demonstrates getting a `Table` object with the desired setting:\n\n```python\nfrom astrapy.api_options import APIOptions, SerdesOptions\nfrom astrapy.constants import MapEncodingMode\n\nmy_options = APIOptions(serdes_options=SerdesOptions(\n    encode_maps_as_lists_in_tables=MapEncodingMode.ALWAYS  # or even just \"ALWAYS\"\n))\n\nmy_table = my_database.get_table(\"my_table\", spawn_api_options=my_options)\n```\n\nSee the section about API Options, and the docstring, for more details.\n\n_Caution: if one plans to use regular Python `dict` objects to express user-defined types (UDTs),_\n_setting this option to ALWAYS would interfere with the format expected by the API for UDTs._\n_See the section on UDTs for more details._\n\n#### User-defined types (UDTs)\n\nThe Data API allows creation of \"user-defined types\" (UDTs), composite data types\nthat can be subsequently used as table column types.\n\n```python\nfrom astrapy.info import (\n    ColumnType,\n    CreateTableDefinition,\n    CreateTypeDefinition,\n    TableScalarColumnTypeDescriptor,\n)\n\nplayer_udt_def = CreateTypeDefinition(\n    fields={\n        \"name\": TableScalarColumnTypeDescriptor(ColumnType.TEXT),\n        \"age\": TableScalarColumnTypeDescriptor(ColumnType.INT),\n    },\n)\nmy_database.create_type(\"player_udt\", definition=player_udt_def)\n\ntable_definition = (\n    CreateTableDefinition.builder()\n    .add_column(\"match_id\", ColumnType.TEXT)\n    .add_userdefinedtype_column(\"player1\", \"player_udt\")\n    .add_userdefinedtype_column(\"player2\", \"player_udt\")\n    .add_partition_by([\"match_id\"])\n    .build()\n)\nudt_table = my_database.create_table(\"matches\", definition=table_definition)\n```\n\nTo write data to UDT columns, the default settings (in part. the `SerdesOptions` settings)\nadmit using plain Python dictionaries. When reading from a `Table`, by default the\nUDT will be returned in the form of an `astrapy.data_types.DataAPIDictUDT`, a subclass\nof `dict`.\n\nThe default write behaviour can be changed through the\n`serdes_options.encode_maps_as_lists_in_tables` API Option,\ndescribed in the \"Maps as association lists\" section.\n\nThe default read behaviour can be changed by setting `serdes_options.custom_datatypes_in_reading`\nto False, in which case UDT data will be returned as a regular `dict`.\n\nThe following code demonstrates the default read and write behaviour, using the `udt_table` just created:\n\n```python\nfrom astrapy.data_types import DataAPIDictUDT\n\n# both are valid ways to express a 'player_udt':\nudt_table.insert_one({\n    \"match_id\": \"x001\",\n    \"player1\": {\"name\": \"Anita\", \"age\": 49},\n    \"player2\": DataAPIDictUDT({\"name\": \"Pedro\", \"age\": 50}),\n})\n# Conversely, use of DataAPIMap for UDTs is an error under the default serdes settings.\n```\n\n_Note that if the map list-encoding is set to \"ALWAYS\", the above insertion\nwill fail because of `\"player1\"`: usage of `DataAPIDictUDT` is guaranteed to work\nin all circumstances instead._\n\n##### Using models for UDTs\n\nIn many cases, one wants to 'bind' a certain UDT to a model class. AstraPy provides\na way to \"register\" model classes both for the write and read paths, through the following\nserdes API Options:\n\n- `serdes_options.serializer_by_class`: a map from _classes_ (not class names; not class instances) to serializer functions. A serializer function accepts an instance of the model class and returns a corresponding `dict` representation;\n- `serdes_options.deserializer_by_udt`: a map from _UDT names_ to deserializer functions. A deserializer function accepts a dict representation, along with the UDT definition, and returns an instance of the model class.\n\nThe following example demonstrates this procedure, including ser/deserializers.\nSuppose you have a `dataclass` corresponding to `player_udt`:\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass Player:\n    name: str\n    age: int\n```\n\nThe two required ser/des functions can be defined and configured for the API options in order to spawn a `Table` object able to use `Player`:\n\n```python\nfrom typing import Any\n\nfrom astrapy.api_options import APIOptions, SerdesOptions\nfrom astrapy.info import CreateTypeDefinition\n\ndef player_serializer(pl: Player) -\u003e dict[str, Any]:\n    # the logic in this function will depend on the model class being used:\n    return pl.__dict__\n\ndef player_deserializer(\n    pl_dict: dict[str, Any],\n    udt_def: CreateTypeDefinition | None,\n) -\u003e Player:\n    # the logic in this function will depend on the model class being used:\n    return Player(**pl_dict)\n\nmy_options = APIOptions(serdes_options=SerdesOptions(\n    serializer_by_class={Player: player_serializer},\n    deserializer_by_udt={\"player_udt\": player_deserializer},\n))\n\n# This statement does not create the table on DB, that is assumed to exist already:\nmy_model_capable_table = database.get_table(\"matches\", spawn_api_options=my_options)\n```\n\nAt this point, writes and reads can seamlessly use the `Player` class:\n\n```python\nmy_model_capable_table.insert_one({\n    \"match_id\": \"x001\",\n    \"player1\": Player(name=\"Anita\", age=49),\n    \"player2\": Player(name=\"Pedro\", age=50),\n})\n\nthe_match = my_model_capable_table.find_one({\"match_id\": \"x001\"})\n# the_match[\"player1\"] and the_match[\"player2\"] are Player objects:\n\nprint(the_match[\"player1\"])\n# prints: Player(name='Anita', age=49)\nprint(the_match[\"player2\"])\n# prints: Player(name='Pedro', age=50)\n```\n##### Summary for UDT usage and maps in Tables\n\nUnder the default serdes settings, the following rules apply and constitute the suggested approach:\n\n- use DataAPIMap to write map columns;\n- use DataAPIDictUDTs, or plain `dict`, to write UDTs;\n- expect DataAPIMap when reading map columns;\n- expect DataAPIDictUDTs when reading UDTs;\n- to use model classes, associate them for reads and writes correspondingly.\n\nThe following tables summarize the interplay between `dict`, `DataAPIMap`, `DataAPIDictUDT` and the\nserdes options for writes and reads in Tables:\n\n_Items in insertions vs. `serdes_options.encode_maps_as_lists_in_tables`._\n(\"D\" = `dict` in payload, \"L\" = list of pairs in payload.)\n\n| Item being written | NEVER | **DATAAPIMAPS** (default) | ALWAYS |\n| --- | --- | --- | --- |\n| DataAPIDictUDT                      | ok, D | ok, D | ok, D |\n| dict (for UDT)                      | ok, D | ok, D | NO (L: rejected) |\n| dict (for map, string keys)         | ok, D | ok, D | ok, L |\n| dict (for map, nonstring keys)      | NO [1] | NO [1] | ok, L |\n| `MyClass`, in `serializer_by_class` | ok, D [2] | ok, D [2] | ok, D [2] |\n| `MyClass`, no registered serializer | NO [3] | NO [3] | NO [3] |\n\nNotes:\n\n1. The JSON serialization would silently convert the keys to string, leading to a type-mismatch API error upon insertion.\n2. In this case the `dict` is the result of the serializer function, of course.\n3. An error _\"Object of type `\u003cclassname\u003e` is not JSON serializable\"_ is raised.\n\n_Items in reads vs. `serdes_options.custom_datatypes_in_reading`._\n(\"M\" = `DataAPIMap` found in row, \"U\" = `DataAPIDictUDT` found in row, \"D\" = `dict` found in row.)\n\n| Item being read | **True** (default) | False |\n| --- | --- | --- |\n| map (as object, string keys)              | ok, M | ok, D |\n| map (as list, string keys)                | ok, M | ok, D |\n| map (as list, nonstring keys)             | ok, M | ok, D [4] |\n| UDT, with entry in `deserializer_by_udt`  | ok [5] | ok [5] |\n| UDT, no registered deserializer           | ok, U | ok, D |\n\nNotes:\n\n4. Caution: the returned regular Python `dict` will have nonstring keys.\n5. The class of the result is whatever the deserializer returns.\n\n### Usage with HCD and other non-Astra installations\n\nThe main difference when targeting e.g. a Hyper-Converged Database (HCD)\ninstallation is how the client is\ninitialized. Here is a short example showing just how to get to a `Database`\n(what comes next is unchaged compared to using Astra DB).\n\n```python\nfrom astrapy import DataAPIClient\nfrom astrapy.constants import Environment\nfrom astrapy.authentication import UsernamePasswordTokenProvider\n\n\n# Build a token\ntp = UsernamePasswordTokenProvider(\"username\", \"password\")\n\n# Initialize the client and get a \"Database\" object\nclient = DataAPIClient(environment=Environment.HCD)\ndatabase = client.get_database(\"http://localhost:8181\", token=tp)\n```\n\nFor more on this case, please consult the [dedicated reference](https://docs.datastax.com/en/hyper-converged-database/1.0/connect/python-client.html).\n\n## AstraPy's API\n\n### Abstraction diagram\n\nAstraPy's abstractions for working at the data and admin layers are structured\nas depicted by this diagram:\n\n![AstraPy, abstractions chart](https://raw.githubusercontent.com/datastax/astrapy/main/pictures/astrapy_abstractions.png)\n\nHere's a small admin-oriented example:\n\n```python\nfrom astrapy import DataAPIClient\n\n\n# this must have \"Database Administrator\" permissions:\nASTRA_DB_APPLICATION_TOKEN = \"AstraCS:...\"\n\nmy_client = DataAPIClient(ASTRA_DB_APPLICATION_TOKEN)\n\nmy_astra_admin = my_client.get_admin()\n\ndatabase_list = list(my_astra_admin.list_databases())\n\ndb_info = database_list[0].info\nprint(db_info.name, db_info.id, db_info.region)\n\nmy_database_admin = my_astra_admin.get_database_admin(db_info.id)\n\nmy_database_admin.list_keyspaces()\nmy_database_admin.create_keyspace(\"my_dreamspace\")\n```\n\n### Exceptions\n\nThe package comes with its own set of exceptions, arranged in this hierarchy:\n\n![AstraPy, exception hierarchy](https://raw.githubusercontent.com/datastax/astrapy/main/pictures/astrapy_exceptions.png)\n\nFor more information, and code examples, check out the docstrings and consult\nthe API reference linked above.\n\n### API Options\n\nYou can configure many aspects of the interaction with the API by providing\ncustomized \"API Options\" objects when either spawning a client, copying objects,\nor spawning \"children classes\" (such as a Table from a Database).\n\nFor the details, please check the docstring for `astrapy.api_options.APIOptions`\nand the other classes in that module. Here is a small example script to show a\npractical starting point:\n\n```python\nfrom astrapy import DataAPIClient\nfrom astrapy.api_options import (\n    APIOptions,\n    SerdesOptions,\n)\n\n# Disable custom datatypes in all reads:\nno_cdt_options = APIOptions(\n    serdes_options=SerdesOptions(\n        custom_datatypes_in_reading=False,\n    )\n)\nmy_client = DataAPIClient(api_options=no_cdt_options)\n\n# These spawned objects inherit that setting:\nmy_database = my_client.get_database(\n    \"https://...\",\n    token=\"my-token-1\",\n)\nmy_table = my_database.get_table(\"my_table\")\n```\n\n### Working with dates in Collections and Tables\n\nDate and datetime objects, i.e. instances of the standard library\n`datetime.datetime` and `datetime.date` classes, can be used\nanywhere when sending documents and queries to the API.\n\nBy default, what you get back is an instance of `astrapy.data_types.DataAPITimestamp`\n(which has a much wider range of expressable timestamps than Python's stdlib).\nIf you want to revert to using the standard library `datetime.datetime`, you can do so\nby turn on the `APIOptions.SerdesOptions.custom_datatypes_in_reading` API Options setting for the\ncollection/table object (note that this setting affects the returned format for several other table data types).\n\nIf you choose to have timestamps returned as standard-library `datetime.datetime` objects,\nboth for collections and tables, you may supply a specific timezone for these\n(the default is UTC). You do so by providing an appropriate `datetime.timezone` value\nto the `APIOptions.SerdesOptions.datetime_tz` API Options setting for the\ncollection/table object. You can also specify `None` for a timezone, in which case\nthe resulting values will be timezone-unaware (or \"naive\") datetimes.\n\n_Naive_ datetimes (i.e. those without a timezone information attached)\nare inherently ambiguous when it comes to translating them into a unambiguous timestamp.\nFor this reason, if you want to work with naive datetimes, and in particular you want\nAstraPy to accept them for writes, you need to explicitly\nturn on the `APIOptions.SerdesOptions.accept_naive_datetimes` API Options setting for the\ncollection/table object, otherwise AstraPy will raise an error.\n\n_Remember that what effectively gets_\n_written to DB is always a (numeric) **timestamp**: for naive quantities, this timestamp value depends_\n_on the implied timezone used in the conversion, potentially leading to unexpected results_\n_e.g. if multiple applications are running with different locale settings._\n\nThe following diagram summarizes the behaviour of the write and read paths for datetime objects,\ndepending on the `SerdesOptions` settings:\n\n![AstraPy, abstractions chart](https://raw.githubusercontent.com/datastax/astrapy/main/pictures/astrapy_datetime_serdes_options.png)\n\nHere an example code snippet showing how to switch to having reads return regular `datetime` objects\nand have them set to one's desired timezone offset:\n\n```python\nfrom datetime import timezone,timedelta\n\nfrom astrapy import DataAPIClient\nfrom astrapy.api_options import APIOptions, SerdesOptions\n\nmy_timezone = timezone(timedelta(hours=4, minutes=30))\n\nmy_client = DataAPIClient()\nmy_database = my_client.get_database(\n    ASTRA_DB_API_ENDPOINT,\n    token=ASTRA_DB_APPLICATION_TOKEN,\n    spawn_api_options=APIOptions(\n        serdes_options=SerdesOptions(\n            custom_datatypes_in_reading=False,\n            datetime_tzinfo=my_timezone,\n        ),\n    ),\n)\n\nmy_collection = my_database.get_collection(\"my_collection\")\n# This document will have datetimes set to the desired timezone\ndocument = my_collection.find_one({\"code\": 123})\n```\n\n### Working with ObjectIds and UUIDs in Collections\n\nAstrapy repackages the ObjectId from `bson` and the UUID class and utilities\nfrom the `uuid` package and its `uuidv6` extension. You can also use them directly.\n\nEven when setting a default ID type for a collection, you still retain the freedom\nto use any ID type for any document:\n\n```python\nfrom astrapy import DataAPIClient\nfrom astrapy.constants import DefaultIdType\nfrom astrapy.ids import ObjectId, uuid8, UUID\n\nimport bson\n\nASTRA_DB_APPLICATION_TOKEN = \"AstraCS:...\"\nASTRA_DB_API_ENDPOINT = \"https://01234567-....apps.astra.datastax.com\"\n\nmy_client = DataAPIClient()\nmy_database = my_client.get_database(\n    ASTRA_DB_API_ENDPOINT,\n    token=ASTRA_DB_APPLICATION_TOKEN,\n)\n\nmy_collection = my_database.create_collection(\n    \"ecommerce\",\n    definition=CollectionDefinition.builder().set_default_id(\n        DefaultIdType.UUIDV6\n    ).build(),\n)\n\nmy_collection.insert_one({\"_id\": ObjectId(\"65fd9b52d7fabba03349d013\")})\nmy_collection.find({\n    \"_id\": UUID(\"018e65c9-e33d-749b-9386-e848739582f0\"),\n})\n\nmy_collection.update_one(\n    {\"tag\": \"in_stock\"},\n    {\"$set\": {\"inventory_id\": bson.objectid.ObjectId()}},\n    upsert=True,\n)\n\nmy_collection.insert_one({\"_id\": uuid8()})\n```\n\n### Escaping field names\n\nField names containing special characters (`.` and `\u0026`) must be correctly escaped\nin certain Data API commands. It is a responsibility of the user to ensure escaping\nis done when needed; however, AstraPy offers utilities to escape sequences of \"path\nsegments\" and -- should it ever be needed -- unescape path-strings back into\nliteral segments:\n\n```python\nfrom astrapy.utils.document_paths import escape_field_names, unescape_field_path\n\nprint(escape_field_names(\"f1\", \"f2\", 12, \"g.\u00263\"))\n# prints: f1.f2.12.g\u0026.\u0026\u00263\nprint(escape_field_names([\"f1\", \"f2\", 12, \"g.\u00263\"]))\n# prints: f1.f2.12.g\u0026.\u0026\u00263\nprint(unescape_field_path(\"a\u0026\u0026\u0026.b.c.d.12\"))\n# prints: ['a\u0026.b', 'c', 'd', '12']\n```\n\n## For contributors\n\nFirst install `uv` (e.g. `pipx install uv`), then set up a dev environment with `make venv`, or equivalently:\n\n```\nuv venv\nuv sync --dev\n```\n\nLinter, style and typecheck should all pass for a PR:\n\n```bash\nmake format\n```\n\nWith `make format-fix` the style and imports are autofixed (by `ruff`)\n\nFeatures must be thoroughly covered in tests (have a look at `tests/*` to infer\nnaming convention and module structure).\n\n### Running tests\n\n#### Typical testing\n\nIn most cases you want to run the \"base\" test suite (the one in the CI/CD automation) against either Astra DB or a local Data API + HCD.\n\nSteps:\n\n- Export variables as in one of the `tests/env_templates/*.base.template` examples.\n- Export variables as in the `tests/env_templates/env.vectorize-minimal.template` example.\n- Run: `uv venv --python \"\u003e=3.8\u003c3.13\" \u0026\u0026 uv run pytest tests/base`\n\n#### All available tests/targets\n\nTests are grouped in:\n- \"base\", covering general-purpose astrapy functionality. Divided in unit/integration;\n- \"vectorize\", extensively running a base workload on all provider/integration choices;\n- \"admin\", doing a whole sweep of admin operations. Very slow on Astra DB.\n\nAstrapy's CI only runs \"base\". The others are to be checked manually when it's needed.\n\nTests can be run on three types of Data API _targets_ (with slight differences in what is applicable):\n\n- **Astra**: an Astra DB target account (or two, as some tests are specific to dev environment)\n- **Local**: a ready-to-use (user-supplied) local Data API on top of a DSE/HCD (e.g. using `tests/hcd_compose`).\n- **DockerCompose**: HCD+Data API, started by the test itself on startup. This is used in the Github action `local` workflow. _Note that in this case the containers created will not be automatically destroyed._\n\nDepending on the target chosen, different environment variables are needed: refer to\nthe `tests/env_templates/*.base.template` examples.\nNote that the variables defined in the desired \"base\" template **must** be set to run test, even for unit tests.\n\nAdditionally, you will need to define the environment variables in `tests/env_templates/env.vectorize-minimal.template`,\nwhich are needed by the minimal set of \"vectorize\" testing belonging to the \"base\" test group.\n\nFor Astra DB, you can include \"shared secret\" vectorize tests (i.e. KMS-based authentication).\nTo run those tests, you must scope an OpenAI API key\nto the target Astra DB with secret name `\"SHARED_SECRET_EMBEDDING_API_KEY_OPENAI\"`\nand comment the environment flag that suppresses them (see the base Astra env template).\n\nFor non-Astra, the reranking-related tests run only if one sets\n`HEADER_RERANKING_API_KEY_NVIDIA=\"AstraCS:\u003cdev token...\u003e` (as shown in the Local/DockerCompose base env templates).\n\n#### Docker vs. Podman\n\nIn case you use a different Docker-compatible container runtime (e.g. `podman`) and are running against the\n\"DockerCompose\" target make sure to export the environment variable such as `DOCKER_COMMAND_NAME=\"podman\"`\nto maek the test startup logic work properly.\n\n#### Keyspaces\n\nYou shoud never need to worry about keyspaces. Tests use two keyspaces, which are created if not found, with default names.\nThe env templates show how to override those names, if you want to.\n\n#### Multiple Python versions\n\nIf may be useful to run e.g. unit tests with multiple Python versions. You can have `uv`\ncreate more than one venv and specify the version, e.g. for each one:\n\n```\nuv venv --python 3.8 .venv-3.8\n. .venv-3.8/bin/activate\nuv sync --dev --active\n```\n\nThen, with the desired virtual env active, you will run e.g. `uv run --active pytest [...]`.\n\nMost make targets will also support running in the named virtual env:\nassuming you activated a certain virtual env, you can run e.g.: `make format VENV=true`.\n\n**Warning: Python 3.13+ currently not supported to run integration tests! (but the package itself is all right).**\n\n#### Adding/changing dependencies\n\nAfter editing the `pyproject.toml`, make sure you run\n\n```\nuv lock\nuv sync --dev\n```\n\nand then commit the new `uv.lock` to the repo as well.\n\n#### Sample testing commands\n\nBase:\n\n```\n# choose one:\nuv run pytest tests/base\nuv run pytest tests/base/unit\nuv run pytest tests/base/integration\n```\n\nAdmin:\n\n```\n# depending on the environment, different 'admin tests' will run:\nuv run pytest tests/admin\n```\n\nExtended vectorize:\n\n```\n# very many env. variables required for this one:\nuv run pytest tests/vectorize\n\n# restrict to some combination(s) with e.g.:\nEMBEDDING_MODEL_TAGS=\"openai/text-embedding-3-large/HEADER/0,voyageAI/voyage-finance-2/SHARED_SECRET/f\" \\\n    uv run pytest tests/vectorize/integration/test_vectorize_providers.py \\\n    -k test_vectorize_usage_auth_type_header_sync\n```\n\nAll the usual `pytest` ways of restricting the test selection hold\n(e.g. `uv run pytest tests/idiomatic/unit` or `[...] -k \u003ctest_name_selector\u003e`). Also e.g.:\n\n```\n# suppress log noise\nuv run pytest [...] -o log_cli=0\n\n# increase log level\nuv run pytest [...] -o log_cli=1 --log-cli-level=10\n```\n\n### Special tests\n\nThe following are special provision to manage features under evolution or not\nentirely deployed to all environments. Typically they require manually passing\ncertain environment variables, otherwise the associated tests are excluded from CI.\n\n#### Cutting-edge features on `main`\n\nPrepend tests with a `ASTRAPY_TEST_LATEST_MAIN=y` for features found on `main` that are not released anywhere.\n_(Tip: run a code search first to see what is currently marked as such. Chances are nothing is.)_\n\n## Appendices\n\n### Appendix A: quick reference for key imports\n\n_Note: check `tests/base/unit/test_imports.py` for more._\n\nClient, data and admin abstractions\n\n```python\nfrom astrapy import (\n    AstraDBAdmin,\n    AstraDBDatabaseAdmin,\n    AsyncCollection,\n    AsyncDatabase,\n    AsyncTable,\n    Collection,\n    Database,\n    DataAPIClient,\n    DataAPIDatabaseAdmin,\n    Table,\n)\n```\n\nConstants for data-related use:\n\n```python\nfrom astrapy.constants import (\n    DefaultIdType,\n    Environment,\n    MapEncodingMode,\n    ReturnDocument,\n    SortMode,\n    VectorMetric,\n)\n```\n\nCursor for find-like operations:\n\n```python\nfrom astrapy.cursors import (\n  AbstractCursor,\n  AsyncCollectionFindAndRerankCursor,\n  AsyncCollectionFindCursor,\n  AsyncTableFindCursor,\n  CollectionFindAndRerankCursor,\n  CollectionFindCursor,\n  CursorState,\n  RerankedResult,\n  TableFindCursor,\n)\n```\n\nObjectIds and UUIDs:\n\n```python\nfrom astrapy.ids import (\n    UUID,\n    ObjectId,\n    uuid1,\n    uuid3,\n    uuid4,\n    uuid5,\n    uuid6,\n    uuid7,\n    uuid8,\n)\n```\n\nAPI Options:\n\n```python\nfrom astrapy.api_options import (\n    APIOptions,\n    DataAPIURLOptions,\n    DevOpsAPIURLOptions,\n    SerdesOptions,\n    TimeoutOptions,\n)\n```\n\nData types:\n\n```python\nfrom astrapy.data_types import (\n    DataAPIDate,\n    DataAPIDuration,\n    DataAPIMap,\n    DataAPISet,\n    DataAPITime,\n    DataAPITimestamp,\n    DataAPIDictUDT,\n    DataAPIVector,\n)\n```\n\nInfo/metadata classes:\n\n```python\nfrom astrapy.info import (\n    AlterTableAddColumns,\n    AlterTableAddVectorize,\n    AlterTableDropColumns,\n    AlterTableDropVectorize,\n    AlterTypeAddFields,\n    AlterTypeOperation,\n    AlterTypeRenameFields,\n    AstraDBAdminDatabaseInfo,\n    AstraDBDatabaseInfo,\n    CollectionDefaultIDOptions,\n    CollectionDefinition,\n    CollectionDescriptor,\n    CollectionInfo,\n    CollectionLexicalOptions,\n    CollectionRerankOptions,\n    CollectionVectorOptions,\n    ColumnType,\n    CreateTableDefinition,\n    CreateTypeDefinition,\n    EmbeddingProvider,\n    EmbeddingProviderAuthentication,\n    EmbeddingProviderModel,\n    EmbeddingProviderParameter,\n    EmbeddingProviderToken,\n    FindEmbeddingProvidersResult,\n    FindRerankingProvidersResult,\n    ListTableDefinition,\n    ListTableDescriptor,\n    ListTypeDescriptor,\n    RerankingProvider,\n    RerankingProviderAuthentication,\n    RerankingProviderModel,\n    RerankingProviderParameter,\n    RerankingProviderToken,\n    RerankServiceOptions,\n    TableAPIIndexSupportDescriptor,\n    TableAPISupportDescriptor,\n    TableBaseIndexDefinition,\n    TableIndexDefinition,\n    TableIndexDescriptor,\n    TableIndexOptions,\n    TableInfo,\n    TableKeyValuedColumnType,\n    TableKeyValuedColumnTypeDescriptor,\n    TablePrimaryKeyDescriptor,\n    TableScalarColumnTypeDescriptor,\n    TableTextIndexDefinition,\n    TableTextIndexOptions,\n    TableUnsupportedColumnTypeDescriptor,\n    TableUnsupportedIndexDefinition,\n    TableUDTColumnDescriptor,\n    TableValuedColumnType,\n    TableValuedColumnTypeDescriptor,\n    TableVectorColumnTypeDescriptor,\n    TableVectorIndexDefinition,\n    TableVectorIndexOptions,\n    VectorServiceOptions,\n)\n```\n\nAuthentication:\n\n```python\nfrom astrapy.authentication import (\n    StaticTokenProvider,\n    UsernamePasswordTokenProvider,\n    EmbeddingAPIKeyHeaderProvider,\n    AWSEmbeddingHeadersProvider,\n)\n```\n\nMiscellaneous utilities:\n\n```python\n# Parsing API Endpoints for Astra DB:\nfrom astrapy.admin import (\n    ParsedAPIEndpoint,\n    parse_api_endpoint,\n)\n\n# Escaping/unescaping document paths:\nfrom astrapy.utils.document_paths import (\n    escape_field_names,\n    unescape_field_path,\n)\n\n# API Options defaults:\nfrom astrapy.utils.api_options import defaultAPIOptions\n```\n\n### Appendix B: compatibility with pre-1.0.0 library\n\nIf your code still uses the pre-1.0.0 astrapy (i.e. `from astrapy.db import AstraDB, AstraDBCollection` and so on)\nyou are strongly advised to migrate to the current API. All of the astrapy pre-1.0 API (later dubbed \"core\")\nworks throughout *astrapy v1*, albeit with a deprecation warning on astrapy v. 1.5.\n\nVersion 2 drops \"core\" support entirely. In order to use astrapy version 2.0+, you need to migrate your application.\nCheck the links at the beginning of this README for the updated documentation and API reference.\n\nCheck out previous versions of this README for more on \"core\": [1.5.2](https://github.com/datastax/astrapy/blob/4601c5fa749925d961de1f114ca27690d1a71b13/README.md) and [pre-1.0](https://github.com/datastax/astrapy/blob/cd3f5ce8146093e10a095709c0f5c3f8e3f2c7da/README.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatastax%2Fastrapy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatastax%2Fastrapy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatastax%2Fastrapy/lists"}