Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b14264d
RDBC-1017 New constants
poissoncorp Mar 2, 2026
07d331e
RDBC-1017 RemoteAttachmentFlags Enum + Remote Attachment Settings Cla…
poissoncorp Mar 2, 2026
86cc148
RDBC-1017 RemoteAttachmentParameters
poissoncorp Mar 2, 2026
7bb649c
RDBC-1017 PutAttachmentCommandData + StoreAttachmentParameters + Atta…
poissoncorp Mar 2, 2026
f1df218
RDBC-1017 GetAttachmentOperation: Read Remote Parameters from Response
poissoncorp Mar 2, 2026
e30a762
RDBC-1017 Session API: store_attachment with StoreAttachmentParameters
poissoncorp Mar 2, 2026
0152f1e
RDBC-1017 Add remote attachments support, AI exception hierarchy, sch…
poissoncorp Mar 2, 2026
1299927
RDBC-1017 Remote attachments bulk insert
poissoncorp Mar 4, 2026
00f750a
RDBC-1017 Remote attachments tests & cleanup
poissoncorp Mar 4, 2026
f708da3
RDBC-1017 Test delete bulk attachments
poissoncorp Mar 4, 2026
b60d6da
RDBC-1017 Schema Validation
poissoncorp Mar 5, 2026
c881ee2
RDBC-1017 Schema Validation Indexing tests
poissoncorp Mar 5, 2026
e592cd0
RDBC-1017 Code formatter
poissoncorp Mar 5, 2026
b72fb72
RDBC-1017 Bump version to v7.2.0
poissoncorp Mar 5, 2026
c5ca51d
RDBC-1017 Skip tests that require license
poissoncorp Mar 5, 2026
1e857e6
RDBC-1017 Test against 7.2 only
poissoncorp Mar 5, 2026
929a183
RDBC-1017 Change datetime.now(UTC) to utcnow to support Python 3.10 a…
poissoncorp Mar 6, 2026
0cfd03a
Merge pull request #283 from poissoncorp/RDBC-1017
poissoncorp Mar 6, 2026
24032b6
RDBC-1029: fix AttributeError in BulkInsert._get_exception_from_opera…
redknightlois Feb 25, 2026
cc596aa
RDBC-1030: normalize Enum dict keys in Utils.entity_to_dict
redknightlois Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/RavenClient.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: tests/python

on:
push:
branches: [v7.1]
branches: [v7.2]
pull_request:
branches: [v7.1]
branches: [v7.2]
schedule:
- cron: '0 10 * * *'
workflow_dispatch:
Expand All @@ -30,7 +30,7 @@ jobs:
strategy:
matrix:
python-version: [ '3.10' ,'3.11', '3.12', '3.13', '3.14']
serverVersion: [ '7.1', '7.2' ]
serverVersion: [ '7.2' ]
fail-fast: false

steps:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pip install ravendb
````

## Introduction and changelog
Python client API (v7.1) for [RavenDB](https://ravendb.net/), a NoSQL document database.
Python client API (v7.2) for [RavenDB](https://ravendb.net/), a NoSQL document database.

**Type-hinted entire project and API results** - using the API is now much more comfortable with IntelliSense

Expand Down
2 changes: 1 addition & 1 deletion README_pypi.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pip install ravendb
```

## Introduction
Python client API (v7.1) for [RavenDB](https://ravendb.net/) , a NoSQL document database.
Python client API (v7.2) for [RavenDB](https://ravendb.net/) , a NoSQL document database.

**Type-hinted entire project and API results** - using the API is now much more comfortable with IntelliSense

Expand Down
30 changes: 21 additions & 9 deletions ravendb/documents/bulk_insert_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from ravendb.json.metadata_as_dictionary import MetadataAsDictionary
from ravendb.documents.commands.batches import CommandType
from ravendb.documents.commands.bulkinsert import GetNextOperationIdCommand, KillOperationCommand
from ravendb.documents.operations.attachments import StoreAttachmentParameters
from ravendb.exceptions.documents.bulkinsert import BulkInsertAbortedException
from ravendb.documents.identity.hilo import GenerateEntityIdOnTheClient
from ravendb.tools.utils import Utils
Expand Down Expand Up @@ -355,7 +356,7 @@ def _get_exception_from_operation(self) -> Optional[BulkInsertAbortedException]:

result = state_request.result["Result"]

if result["$type"].starts_with("Raven.Client.Documents.Operations.OperationExceptionResult"):
if result["$type"].startswith("Raven.Client.Documents.Operations.OperationExceptionResult"):
return BulkInsertAbortedException(result["Error"])

return None
Expand Down Expand Up @@ -615,13 +616,16 @@ def __init__(self, operation: BulkInsertOperation, key: str):
self.key = key

def store(self, name: str, attachment_bytes: bytes, content_type: Optional[str] = None) -> None:
self.operation._attachments_operation.store(self.key, name, attachment_bytes, content_type)
self.store_with_parameters(StoreAttachmentParameters(name, attachment_bytes, content_type=content_type))

def store_with_parameters(self, parameters: StoreAttachmentParameters) -> None:
self.operation._attachments_operation.store(self.key, parameters)

class AttachmentsBulkInsertOperation:
def __init__(self, operation: BulkInsertOperation):
self.operation = operation

def store(self, key: str, name: str, attachment_bytes: bytes, content_type: Optional[str] = None):
def store(self, key: str, parameters: StoreAttachmentParameters):
release_lock_callback = self.operation._concurrency_check()
try:
self.operation._end_previous_command_if_needed()
Expand All @@ -634,22 +638,30 @@ def store(self, key: str, name: str, attachment_bytes: bytes, content_type: Opti
if not self.operation._first:
self.operation._write_comma()

self.operation._first = False
self.operation._in_progress_command = CommandType.NONE

self.operation._write_string_no_escape('{"Id":"')
self.operation._write_string(key)
self.operation._write_string_no_escape('","Type":"AttachmentPUT","Name":"')
self.operation._write_string(name)
self.operation._write_string(parameters.name)

if content_type:
self.operation._write_string_no_escape('","ContentType:"')
self.operation._write_string(content_type)
if parameters.content_type:
self.operation._write_string_no_escape('","ContentType":"')
self.operation._write_string(parameters.content_type)

self.operation._write_string_no_escape('","ContentLength":')
self.operation._write_string_no_escape(str(len(attachment_bytes)))
self.operation._write_string_no_escape(str(len(parameters.stream)))

if parameters.remote_parameters is not None:
self.operation._write_string_no_escape(',"RemoteParameters":')
self.operation._write_string_no_escape(json.dumps(parameters.remote_parameters.to_json()))

self.operation._write_string_no_escape("}")

self.operation._flush_if_needed()

self.operation._current_data_buffer += bytearray(attachment_bytes)
self.operation._current_data_buffer += bytearray(parameters.stream)

self.operation._flush_if_needed()

Expand Down
42 changes: 34 additions & 8 deletions ravendb/documents/commands/batches.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

if TYPE_CHECKING:
from ravendb.documents.conventions import DocumentConventions
from ravendb.documents.operations.attachments import RemoteAttachmentParameters
from ravendb.documents.operations.patch import PatchRequest
from ravendb.documents.session.document_session_operations.in_memory_document_session_operations import (
InMemoryDocumentSessionOperations,
Expand Down Expand Up @@ -117,6 +118,8 @@ def __init__(
if self.__attachment_streams is None:
self.__attachment_streams = []
stream = command.stream
if stream is None:
continue # remote-only attachment — no stream to track
if stream in self.__attachment_streams:
raise RuntimeError(
"It is forbidden to re-use the same stream for more than one attachment. "
Expand All @@ -139,12 +142,13 @@ def create_request(self, node: ServerNode) -> requests.Request:
for command in self.__commands:
if command.command_type == CommandType.ATTACHMENT_PUT:
command: PutAttachmentCommandData
files[command.name] = (
command.name,
command.stream,
command.content_type,
{"Command-Type": "AttachmentStream"},
)
if command.stream is not None:
files[command.name] = (
command.name,
command.stream,
command.content_type,
{"Command-Type": "AttachmentStream"},
)
if not request.data:
request.data = {"Commands": []}
request.data["Commands"].append(command.serialize(self.__conventions))
Expand Down Expand Up @@ -526,7 +530,17 @@ def serialize(self, conventions: DocumentConventions) -> Dict:


class PutAttachmentCommandData(CommandData):
def __init__(self, document_id: str, name: str, stream: bytes, content_type: str, change_vector: str):
def __init__(
self,
document_id: str,
name: str,
stream: bytes,
content_type: str,
change_vector: str,
remote_parameters: Optional["RemoteAttachmentParameters"] = None,
hash: str = None,
size_in_bytes: int = None,
):
if not document_id:
raise ValueError(document_id)
if not name:
Expand All @@ -535,6 +549,9 @@ def __init__(self, document_id: str, name: str, stream: bytes, content_type: str
super(PutAttachmentCommandData, self).__init__(document_id, name, change_vector, CommandType.ATTACHMENT_PUT)
self.__stream = stream
self.__content_type = content_type
self.__remote_parameters = remote_parameters
self.__hash = hash
self.__size_in_bytes = size_in_bytes

@property
def stream(self):
Expand All @@ -544,14 +561,23 @@ def stream(self):
def content_type(self):
return self.__content_type

@property
def remote_parameters(self) -> Optional["RemoteAttachmentParameters"]:
return self.__remote_parameters

def serialize(self, conventions: DocumentConventions) -> dict:
return {
result = {
"Id": self._key,
"Name": self._name,
"ContentType": self.__content_type,
"ChangeVector": self._change_vector,
"Type": str(self._command_type),
"RemoteParameters": self.__remote_parameters.to_json() if self.__remote_parameters is not None else None,
"Hash": self.__hash,
}
if self.__size_in_bytes is not None:
result["SizeInBytes"] = self.__size_in_bytes
return result


class CopyAttachmentCommandData(CommandData):
Expand Down
38 changes: 37 additions & 1 deletion ravendb/documents/indexes/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import datetime
import enum
import re
from enum import Enum
from enum import Enum, IntFlag
from abc import ABC
from typing import Union, Optional, List, Dict, Set, Iterable
from ravendb.documents.indexes.spatial.configuration import SpatialOptions, AutoSpatialOptions
Expand Down Expand Up @@ -140,6 +140,38 @@ def __str__(self):
return self.value


class IndexDefinitionCompareDifferences(IntFlag):
NONE = 0
MAPS = 1 << 0
REDUCE = 1 << 1
FIELDS = 1 << 2
CONFIGURATION = 1 << 3
LOCK_MODE = 1 << 4
PRIORITY = 1 << 5
STATE = 1 << 6
ADDITIONAL_SOURCES = 1 << 7
ADDITIONAL_ASSEMBLIES = 1 << 8
DEPLOYMENT_MODE = 1 << 12
COMPOUND_FIELDS = 1 << 13
ARCHIVED_DATA_PROCESSING_BEHAVIOR = 1 << 14
SCHEMA_VALIDATION_CONFIGURATION = 1 << 15
ALL = (
MAPS
| REDUCE
| FIELDS
| CONFIGURATION
| LOCK_MODE
| PRIORITY
| STATE
| ADDITIONAL_SOURCES
| ADDITIONAL_ASSEMBLIES
| DEPLOYMENT_MODE
| COMPOUND_FIELDS
| ARCHIVED_DATA_PROCESSING_BEHAVIOR
| SCHEMA_VALIDATION_CONFIGURATION
)


class GroupByArrayBehavior(Enum):
NOT_APPLICABLE = "NotApplicable"
BY_CONTENT = "ByContent"
Expand Down Expand Up @@ -205,6 +237,7 @@ def __init__(
pattern_references_collection_name: Optional[str] = None,
deployment_mode: Optional[IndexDeploymentMode] = None,
search_engine_type: Optional[SearchEngineType] = None,
schema_definitions: Optional[Dict[str, str]] = None,
):
super(IndexDefinition, self).__init__(name, priority, state)
self.lock_mode = lock_mode
Expand All @@ -222,6 +255,7 @@ def __init__(
self.pattern_references_collection_name = pattern_references_collection_name
self.deployment_mode = deployment_mode
self.search_engine_type = search_engine_type
self.schema_definitions = schema_definitions

@classmethod
def from_json(cls, json_dict: dict) -> IndexDefinition:
Expand Down Expand Up @@ -255,6 +289,7 @@ def from_json(cls, json_dict: dict) -> IndexDefinition:
deploy = json_dict.get("DeploymentMode", None)
if deploy is not None:
result.deployment_mode = IndexDeploymentMode(deploy)
result.schema_definitions = json_dict.get("SchemaDefinitions")
return result

def to_json(self) -> dict:
Expand All @@ -278,6 +313,7 @@ def to_json(self) -> dict:
"PatternForOutputReduceToCollectionReferences": self.pattern_for_output_reduce_to_collection_references,
"PatternReferencesCollectionName": self.pattern_references_collection_name,
"DeploymentMode": self.deployment_mode,
"SchemaDefinitions": self.schema_definitions,
}

@property
Expand Down
Loading