objectstore_client package
- class objectstore_client.Client(base_url: str, metrics_backend: MetricsBackend | None = None, retries: int | None = None, timeout_ms: float | None = None, connection_kwargs: Mapping[str, Any] | None = None, token: TokenGenerator | str | None = None, spool_dir: str | PathLike[str] | None = None)[source]
Bases:
objectA client for Objectstore. Constructing it initializes a connection pool.
- Parameters:
base_url – The base URL of the Objectstore server (e.g., “http://objectstore:8888”). metrics_backend: Optional metrics backend for tracking storage operations. Defaults to
NoOpMetricsBackendif not provided.retries – Number of connection retries for failed requests. Defaults to
3if not specified. Note: only connection failures are retried, not read failures (as compression streams cannot be rewound).timeout_ms –
Read timeout in milliseconds for API requests. The read timeout is the maximum time to wait between consecutive read operations on the socket (i.e., between receiving chunks of data). Defaults to no read timeout if not specified. The connection timeout is always 100ms. To override the connection timeout, pass a custom
urllib3.Timeoutobject viaconnection_kwargs. For example:client = Client( "http://objectstore:8888", connection_kwargs={ "timeout": urllib3.Timeout(connect=1.0, read=5.0) } )
connection_kwargs – Additional keyword arguments to pass to the underlying urllib3 connection pool (e.g., custom headers, SSL settings, advanced timeouts). By default, requests carry a
User-Agentheader ofobjectstore-client/{version}; pass aheadersmapping with aUser-Agentkey here to override it.token – A
TokenGeneratorthat signs a fresh JWT for each request using an EdDSA keypair, or a static pre-signed JWT string used as-is for every request. Use aTokenGeneratorfor internal services that have access to the signing key, and a string for external services that receive a token from another source.spool_dir – Directory that temporary files are created in when preparing resumable uploads.
Noneuses Python’s default temporary directory.
- session(usecase: Usecase, **scopes: str | int | bool) Session[source]
Create a [Session] with the Objectstore server, tied to a specific [Usecase] and [Scope].
A Scope is a (possibly nested) namespace within a Usecase, given as a sequence of key-value pairs passed as kwargs. IMPORTANT: the order of the kwargs matters!
The admitted characters for keys and values are: A-Za-z0-9_-()$!+’.
Users are free to choose the scope structure that best suits their Usecase. The combination of Usecase and Scope will determine the physical key/path of the blob in the underlying storage backend.
For most usecases, it’s recommended to use the organization and project ID as the first components of the scope, as follows:
` client.session(usecase, org=organization_id, project=project_id, ...) `- Parameters:
usecase – The Usecase to scope this session to.
**scopes – Key-value pairs defining the scope within the usecase.
- exception objectstore_client.ExpiryExtensionRejected(message: str, status: int | None, response: str | None)[source]
Bases:
RequestErrorThe object’s expiration deadline could not be extended.
The object is non-expiring, lacks required creation metadata, or conflicts with a conditional update. A conflict can result from concurrent deletion, so this does not guarantee that the object still exists.
- class objectstore_client.GetResponse(metadata, payload)[source]
Bases:
NamedTuple- payload: IO[bytes]
Alias for field number 1
- class objectstore_client.Metadata(content_type: 'str | None', compression: 'Compression | None', expiration_policy: 'ExpirationPolicy | None', time_created: 'datetime | None', time_expires: 'datetime | None', origin: 'str | None', filename: 'str | None', size: 'int | None', custom: 'dict[str, str]')[source]
Bases:
object- compression: Literal['zstd', 'gzip'] | Literal['none'] | None
- content_type: str | None
- custom: dict[str, str]
- expiration_policy: TimeToIdle | TimeToLive | None
- filename: str | None
An optional filename associated with this object.
When present, the server includes a Content-Disposition header in GET responses, prompting browsers and download tools to save the file under this name.
- origin: str | None
The origin of the object, typically the IP address of the original source.
This tracks where the payload was originally obtained from (e.g., the IP of a Sentry SDK or CLI).
- size: int | None
The size of the complete stored object in bytes.
This is always the size of the whole object, even when only a range of it was requested. For a compressed object it is the compressed size, matching the bytes on the wire.
This field is computed by the server, it cannot be set by clients.
- time_created: datetime | None
Timestamp indicating when the object was created or the last time it was replaced.
This means that a PUT request to an existing object causes this value to be bumped. This field is computed by the server, it cannot be set by clients.
- time_expires: datetime | None
Timestamp indicating when the object will expire.
When using a Time To Idle expiration policy, this value will reflect the expiration timestamp present prior to the current access to the object.
This field is computed by the server. Use expiration_policy to set the policy on upload, or Session.extend_expiry to extend an existing deadline.
- class objectstore_client.MetricsBackend(*args, **kwargs)[source]
Bases:
ProtocolAn abstract class that defines the interface for metrics backends.
- abstractmethod distribution(name: str, value: int | float, tags: Mapping[str, str] | None = None, unit: str | None = None) None[source]
Records a distribution metric.
- class objectstore_client.NoOpMetricsBackend(*args, **kwargs)[source]
Bases:
MetricsBackendDefault metrics backend that does not record anything.
- distribution(name: str, value: int | float, tags: Mapping[str, str] | None = None, unit: str | None = None) None[source]
Records a distribution metric.
- exception objectstore_client.ObjectNotFound(message: str, status: int | None, response: str | None)[source]
Bases:
RequestErrorThe object was observed to be absent or expired.
- class objectstore_client.Permission(value)[source]
Bases:
StrEnumEnum listing permissions that Objectstore tokens may be granted.
- OBJECT_DELETE = 'object.delete'
- OBJECT_READ = 'object.read'
- OBJECT_WRITE = 'object.write'
- exception objectstore_client.RequestError(message: str, status: int | None, response: str | None)[source]
Bases:
ExceptionException raised if an API call to Objectstore fails.
- class objectstore_client.SecretKey(kid: str, secret_key: str)[source]
Bases:
objectAn EdDSA keypair used to authenticate with Objectstore.
kidis the key ID that the Objectstore server uses to load the corresponding public key from its configuration, andsecret_keyis the PEM-encoded EdDSA private key.A
SecretKeyonly identifies the signer. To authenticate requests, wrap it in aTokenGenerator, which adds the expiry and permissions for the tokens it signs.
- class objectstore_client.Session(pool: HTTPConnectionPool, base_path: str, metrics_backend: MetricsBackend, usecase: Usecase, scope: Scope, token: TokenGenerator | str | None = None, spool_dir: str | PathLike[str] | None = None)[source]
Bases:
objectA session with the Objectstore server, scoped to a specific [Usecase] and Scope.
This should never be constructed directly, use [Client.session].
- extend_expiry(key: str, at: datetime | None = None, from_creation: timedelta | None = None, from_now: timedelta | None = None) None[source]
Extend an object’s expiration deadline, preserving its payload.
Supply exactly one target:
atis a timezone-aware absolute datetime;from_creationis total lifetime since creation or replacement; andfrom_nowis lifetime from server request start. Relative targets are resolved by the server, not added to the existing deadline. Fractional duration seconds are truncated; absolute deadlines round upward to seconds.An already-sufficient deadline succeeds without being shortened. Success returns no value and does not indicate whether the deadline changed. Requires object-write permission.
Actual extensions adjust TTL duration to approximately match the total lifetime since creation; TTI duration remains unchanged.
Raises
ValueErrorfor missing or multiple targets, naive datetimes, or negative durations. Zero durations are valid. Objects observed absent or expired raiseObjectNotFound. Rejected extensions raiseExpiryExtensionRejectedfor non-expiring or concurrently changed objects, or missing creation metadata needed for a creation-relative target. Both exceptions subclassRequestError. Rejection does not guarantee that the object still exists, since it may be deleted concurrently. Other HTTP errors propagate normally. Retryingfrom_nowestablishes a new server-time anchor and can extend the deadline further.Example:
from objectstore_client import ExpiryExtensionRejected, ObjectNotFound try: session.extend_expiry(key, from_now=timedelta(days=30)) except ObjectNotFound: print("Object is missing or expired") except ExpiryExtensionRejected: print("Extension was rejected")
- get(key: str, decompress: bool = True, accept_encoding: Sequence[str] | None = None) GetResponse | None[source]
This fetches the blob with the given key, returning an IO stream that can be read.
Returns
Noneif the object does not exist.By default, content that was uploaded compressed will be automatically decompressed, unless decompress=False is passed.
If accept_encoding is provided, any compression algorithm listed there will be passed through compressed instead of decompressed, even when decompress=True. For example, passing accept_encoding=[“zstd”] returns the raw zstd-compressed bytes when the stored object is zstd-compressed.
- head(key: str) Metadata | None[source]
Checks whether an object exists and retrieves its metadata.
Returns the object’s
Metadataif it exists,Noneotherwise.If the object has a TTI expiration policy, this is considered an access, and therefore bumps its expiration.
- mint_token(permissions: list[Permission] | None = None, expiry_seconds: int | None = None) str[source]
Returns a signed token.
When
permissionsisNone, the generator’s default permissions are used. When provided, they must be a subset of the generator’s permissions.When
expiry_secondsisNone, the generator’s default expiry is used.Raises
ValueErrorif noTokenGeneratoris configured or if any requested permission is not granted to the generator.
- object_url(key: str, token_validity: timedelta | None = None, response_content_disposition: Literal['inline', 'attachment'] | None = None) str[source]
Generates a GET url to the object with the given key.
This can then be used by downstream services to fetch the given object. NOTE however that the service does not strictly follow HTTP semantics, in particular in relation to Accept-Encoding.
When
token_validityis provided, read-only authorization information is embedded in the returned URL’s query string, valid for the given duration.response_content_dispositionselectsattachment(default) orinlinefor successful GET/HEAD responses, retaining the stored filename.These options are not bound by the auth token and can be changed by recipients. Use
presigned_object_url()to bind the options to a signature.Raises
ValueErroriftoken_validityis provided but noTokenGeneratoris configured on this session, or if disposition is invalid.
- presigned_object_url(method: Literal['GET', 'HEAD'], key: str, duration: timedelta = datetime.timedelta(seconds=3600), response_content_disposition: Literal['inline', 'attachment'] | None = None) str[source]
Generates a pre-signed URL authorizing a single
methodrequest on the object with the givenkey, valid forduration.Warning
Experimental: pre-signed URLs are an experimental feature and this API may change in a future release.
Raises
ValueErrorif noTokenGeneratoris configured on this session’s client, ifmethodis not supported, or ifdurationis above the one-week maximum, or if disposition is invalid.The returned URL carries a signature that allows the recipient to perform a request without an auth token. It can be handed to any HTTP client; the URL is already percent-encoded, and must be transmitted verbatim.
response_content_dispositionselectsattachment(default) orinlinefor successful GET/HEAD responses, retaining the stored filename.Response options are included in the signature when supplied.
Note that HEAD and GET are considered equivalent from the point of view of signatures, meaning that a signature for GET can also be used for HEAD, and viceversa.
- put(contents: bytes | IO[bytes], key: str | None = None, compress: Literal['zstd', 'gzip'] | Literal['none'] | None = None, precompressed: Literal['zstd', 'gzip'] | Literal['none'] | None = None, content_type: str | None = None, metadata: dict[str, str] | None = None, expiration_policy: TimeToIdle | TimeToLive | None = None, origin: str | None = None, filename: str | None = None, resumable_threshold_bytes: int | Literal['none'] | None = None) str[source]
Uploads the given contents to blob storage.
If no key is provided, one will be automatically generated and returned from this function.
The client will select the Usecase’s configured compression if none is given explicitly. This can be overridden by explicitly giving a compress argument. Providing “none” as the argument will instruct the client to not apply any compression to this upload, which is useful for uncompressible formats.
If contents is already compressed, pass the algorithm as precompressed instead. The payload is then uploaded verbatim and the algorithm is recorded in the object’s metadata, so downloads decompress it transparently.
You can use the utility function objectstore_client.utils.guess_mime_type to attempt to guess a content_type based on magic bytes.
Resumable Uploads
This method uses Objectstore’s Resumable Uploads API when the size of
contentsis at leastresumable_threshold_bytes. Passresumable_threshold_bytes="none"to disable Resumable Uploads. When not overridden, this threshold defaults to the one set on the Usecase.Transient failures and writes that make no progress are retried up to twice. The retry count resets whenever the server reports a higher offset.
For seekable binary streams, the remaining size is measured by seeking to the end and restoring the original position. Prefer streams with efficient seeking: transforming streams may read their entire input to find the end. Non-seekable streams and streams whose size cannot be determined use a regular upload without spooling.
Resumable uploads reuse the input unless compression is needed, in which case the compressed payload is spooled in a temporary file (
tempfile.TemporaryFile) on disk before sending. SetClient(spool_dir=<path>)if you wish to customize the directory where the temporary files are created in.
- objectstore_client.Token
alias of
str
- class objectstore_client.TokenGenerator(key: SecretKey, expiry_seconds: int = 60, permissions: list[Permission] | None = None)[source]
Bases:
objectGenerates auth tokens for Objectstore requests, signed with a
SecretKey.expiry_secondsandpermissionsare the defaults for every token this generator signs. Per-token overrides may shorten the expiry or narrow the permissions, but cannot grant permissions beyondpermissions.Pass a
TokenGeneratordirectly toClient(token=...)and it will sign a fresh JWT for each request. Its key is also used to sign pre-signed URLs.- token_for_scope(usecase: str, scope: Scope, permissions: list[Permission] | None = None, expiry_seconds: int | None = None) str[source]
Sign a JWT for the passed-in usecase and scope using this generator’s key, expiry, and permissions.
When
permissionsisNone, the default permissions are used. When provided, they override the defaults for this token only.When
expiry_secondsisNone, the default expiry is used.Raises
ValueErrorif any requested permission is not granted to this generator.
- class objectstore_client.Usecase(name: str, compression: Literal['zstd', 'gzip', 'none'] = 'zstd', expiration_policy: TimeToIdle | TimeToLive | None = None, resumable_threshold_bytes: int | Literal['none'] = 268435456)[source]
Bases:
objectAn identifier for a workload in Objectstore, along with defaults to use for all operations within that Usecase.
Usecases need to be statically defined in Objectstore’s configuration server-side. Objectstore can make decisions based on the Usecase. For example, choosing the most suitable storage backend.
Names must be 3–64 ASCII characters from
[A-Za-z0-9_-]. Any permitted character may appear in any position. Names are case-sensitive and are never normalized. The name is read-only after construction.- property name: str
The validated usecase name.
- objectstore_client.parse_accept_encoding(header: str) list[str][source]
Parse an Accept-Encoding header value for use in objectstore GET requests.
Returns a list of encoding names, normalized to lowercase and stripped of q-values, per RFC 7231. Encodings explicitly rejected via
q=0are excluded.Note:
identity;q=0is not supported and will be silently ignored (i.e., treated as acceptable), since objectstore always serves an identity fallback.
Submodules
objectstore_client.auth module
- class objectstore_client.auth.Permission(value)[source]
Bases:
StrEnumEnum listing permissions that Objectstore tokens may be granted.
- OBJECT_DELETE = 'object.delete'
- OBJECT_READ = 'object.read'
- OBJECT_WRITE = 'object.write'
- class objectstore_client.auth.SecretKey(kid: str, secret_key: str)[source]
Bases:
objectAn EdDSA keypair used to authenticate with Objectstore.
kidis the key ID that the Objectstore server uses to load the corresponding public key from its configuration, andsecret_keyis the PEM-encoded EdDSA private key.A
SecretKeyonly identifies the signer. To authenticate requests, wrap it in aTokenGenerator, which adds the expiry and permissions for the tokens it signs.
- objectstore_client.auth.Token
A static, pre-signed JWT used as-is for every request.
- class objectstore_client.auth.TokenGenerator(key: SecretKey, expiry_seconds: int = 60, permissions: list[Permission] | None = None)[source]
Bases:
objectGenerates auth tokens for Objectstore requests, signed with a
SecretKey.expiry_secondsandpermissionsare the defaults for every token this generator signs. Per-token overrides may shorten the expiry or narrow the permissions, but cannot grant permissions beyondpermissions.Pass a
TokenGeneratordirectly toClient(token=...)and it will sign a fresh JWT for each request. Its key is also used to sign pre-signed URLs.- token_for_scope(usecase: str, scope: Scope, permissions: list[Permission] | None = None, expiry_seconds: int | None = None) str[source]
Sign a JWT for the passed-in usecase and scope using this generator’s key, expiry, and permissions.
When
permissionsisNone, the default permissions are used. When provided, they override the defaults for this token only.When
expiry_secondsisNone, the default expiry is used.Raises
ValueErrorif any requested permission is not granted to this generator.
- objectstore_client.auth.TokenProvider = objectstore_client.auth.TokenGenerator | str
Authentication provider for Objectstore requests.
Can be either a
TokenGeneratorthat signs a fresh JWT per request, or a static pre-signedToken.
objectstore_client.client module
- class objectstore_client.client.Client(base_url: str, metrics_backend: MetricsBackend | None = None, retries: int | None = None, timeout_ms: float | None = None, connection_kwargs: Mapping[str, Any] | None = None, token: TokenGenerator | str | None = None, spool_dir: str | PathLike[str] | None = None)[source]
Bases:
objectA client for Objectstore. Constructing it initializes a connection pool.
- Parameters:
base_url – The base URL of the Objectstore server (e.g., “http://objectstore:8888”). metrics_backend: Optional metrics backend for tracking storage operations. Defaults to
NoOpMetricsBackendif not provided.retries – Number of connection retries for failed requests. Defaults to
3if not specified. Note: only connection failures are retried, not read failures (as compression streams cannot be rewound).timeout_ms –
Read timeout in milliseconds for API requests. The read timeout is the maximum time to wait between consecutive read operations on the socket (i.e., between receiving chunks of data). Defaults to no read timeout if not specified. The connection timeout is always 100ms. To override the connection timeout, pass a custom
urllib3.Timeoutobject viaconnection_kwargs. For example:client = Client( "http://objectstore:8888", connection_kwargs={ "timeout": urllib3.Timeout(connect=1.0, read=5.0) } )
connection_kwargs – Additional keyword arguments to pass to the underlying urllib3 connection pool (e.g., custom headers, SSL settings, advanced timeouts). By default, requests carry a
User-Agentheader ofobjectstore-client/{version}; pass aheadersmapping with aUser-Agentkey here to override it.token – A
TokenGeneratorthat signs a fresh JWT for each request using an EdDSA keypair, or a static pre-signed JWT string used as-is for every request. Use aTokenGeneratorfor internal services that have access to the signing key, and a string for external services that receive a token from another source.spool_dir – Directory that temporary files are created in when preparing resumable uploads.
Noneuses Python’s default temporary directory.
- session(usecase: Usecase, **scopes: str | int | bool) Session[source]
Create a [Session] with the Objectstore server, tied to a specific [Usecase] and [Scope].
A Scope is a (possibly nested) namespace within a Usecase, given as a sequence of key-value pairs passed as kwargs. IMPORTANT: the order of the kwargs matters!
The admitted characters for keys and values are: A-Za-z0-9_-()$!+’.
Users are free to choose the scope structure that best suits their Usecase. The combination of Usecase and Scope will determine the physical key/path of the blob in the underlying storage backend.
For most usecases, it’s recommended to use the organization and project ID as the first components of the scope, as follows:
` client.session(usecase, org=organization_id, project=project_id, ...) `- Parameters:
usecase – The Usecase to scope this session to.
**scopes – Key-value pairs defining the scope within the usecase.
- class objectstore_client.client.GetResponse(metadata, payload)[source]
Bases:
NamedTuple- payload: IO[bytes]
Alias for field number 1
- class objectstore_client.client.Session(pool: HTTPConnectionPool, base_path: str, metrics_backend: MetricsBackend, usecase: Usecase, scope: Scope, token: TokenGenerator | str | None = None, spool_dir: str | PathLike[str] | None = None)[source]
Bases:
objectA session with the Objectstore server, scoped to a specific [Usecase] and Scope.
This should never be constructed directly, use [Client.session].
- extend_expiry(key: str, at: datetime | None = None, from_creation: timedelta | None = None, from_now: timedelta | None = None) None[source]
Extend an object’s expiration deadline, preserving its payload.
Supply exactly one target:
atis a timezone-aware absolute datetime;from_creationis total lifetime since creation or replacement; andfrom_nowis lifetime from server request start. Relative targets are resolved by the server, not added to the existing deadline. Fractional duration seconds are truncated; absolute deadlines round upward to seconds.An already-sufficient deadline succeeds without being shortened. Success returns no value and does not indicate whether the deadline changed. Requires object-write permission.
Actual extensions adjust TTL duration to approximately match the total lifetime since creation; TTI duration remains unchanged.
Raises
ValueErrorfor missing or multiple targets, naive datetimes, or negative durations. Zero durations are valid. Objects observed absent or expired raiseObjectNotFound. Rejected extensions raiseExpiryExtensionRejectedfor non-expiring or concurrently changed objects, or missing creation metadata needed for a creation-relative target. Both exceptions subclassRequestError. Rejection does not guarantee that the object still exists, since it may be deleted concurrently. Other HTTP errors propagate normally. Retryingfrom_nowestablishes a new server-time anchor and can extend the deadline further.Example:
from objectstore_client import ExpiryExtensionRejected, ObjectNotFound try: session.extend_expiry(key, from_now=timedelta(days=30)) except ObjectNotFound: print("Object is missing or expired") except ExpiryExtensionRejected: print("Extension was rejected")
- get(key: str, decompress: bool = True, accept_encoding: Sequence[str] | None = None) GetResponse | None[source]
This fetches the blob with the given key, returning an IO stream that can be read.
Returns
Noneif the object does not exist.By default, content that was uploaded compressed will be automatically decompressed, unless decompress=False is passed.
If accept_encoding is provided, any compression algorithm listed there will be passed through compressed instead of decompressed, even when decompress=True. For example, passing accept_encoding=[“zstd”] returns the raw zstd-compressed bytes when the stored object is zstd-compressed.
- head(key: str) Metadata | None[source]
Checks whether an object exists and retrieves its metadata.
Returns the object’s
Metadataif it exists,Noneotherwise.If the object has a TTI expiration policy, this is considered an access, and therefore bumps its expiration.
- mint_token(permissions: list[Permission] | None = None, expiry_seconds: int | None = None) str[source]
Returns a signed token.
When
permissionsisNone, the generator’s default permissions are used. When provided, they must be a subset of the generator’s permissions.When
expiry_secondsisNone, the generator’s default expiry is used.Raises
ValueErrorif noTokenGeneratoris configured or if any requested permission is not granted to the generator.
- object_url(key: str, token_validity: timedelta | None = None, response_content_disposition: Literal['inline', 'attachment'] | None = None) str[source]
Generates a GET url to the object with the given key.
This can then be used by downstream services to fetch the given object. NOTE however that the service does not strictly follow HTTP semantics, in particular in relation to Accept-Encoding.
When
token_validityis provided, read-only authorization information is embedded in the returned URL’s query string, valid for the given duration.response_content_dispositionselectsattachment(default) orinlinefor successful GET/HEAD responses, retaining the stored filename.These options are not bound by the auth token and can be changed by recipients. Use
presigned_object_url()to bind the options to a signature.Raises
ValueErroriftoken_validityis provided but noTokenGeneratoris configured on this session, or if disposition is invalid.
- presigned_object_url(method: Literal['GET', 'HEAD'], key: str, duration: timedelta = datetime.timedelta(seconds=3600), response_content_disposition: Literal['inline', 'attachment'] | None = None) str[source]
Generates a pre-signed URL authorizing a single
methodrequest on the object with the givenkey, valid forduration.Warning
Experimental: pre-signed URLs are an experimental feature and this API may change in a future release.
Raises
ValueErrorif noTokenGeneratoris configured on this session’s client, ifmethodis not supported, or ifdurationis above the one-week maximum, or if disposition is invalid.The returned URL carries a signature that allows the recipient to perform a request without an auth token. It can be handed to any HTTP client; the URL is already percent-encoded, and must be transmitted verbatim.
response_content_dispositionselectsattachment(default) orinlinefor successful GET/HEAD responses, retaining the stored filename.Response options are included in the signature when supplied.
Note that HEAD and GET are considered equivalent from the point of view of signatures, meaning that a signature for GET can also be used for HEAD, and viceversa.
- put(contents: bytes | IO[bytes], key: str | None = None, compress: Literal['zstd', 'gzip'] | Literal['none'] | None = None, precompressed: Literal['zstd', 'gzip'] | Literal['none'] | None = None, content_type: str | None = None, metadata: dict[str, str] | None = None, expiration_policy: TimeToIdle | TimeToLive | None = None, origin: str | None = None, filename: str | None = None, resumable_threshold_bytes: int | Literal['none'] | None = None) str[source]
Uploads the given contents to blob storage.
If no key is provided, one will be automatically generated and returned from this function.
The client will select the Usecase’s configured compression if none is given explicitly. This can be overridden by explicitly giving a compress argument. Providing “none” as the argument will instruct the client to not apply any compression to this upload, which is useful for uncompressible formats.
If contents is already compressed, pass the algorithm as precompressed instead. The payload is then uploaded verbatim and the algorithm is recorded in the object’s metadata, so downloads decompress it transparently.
You can use the utility function objectstore_client.utils.guess_mime_type to attempt to guess a content_type based on magic bytes.
Resumable Uploads
This method uses Objectstore’s Resumable Uploads API when the size of
contentsis at leastresumable_threshold_bytes. Passresumable_threshold_bytes="none"to disable Resumable Uploads. When not overridden, this threshold defaults to the one set on the Usecase.Transient failures and writes that make no progress are retried up to twice. The retry count resets whenever the server reports a higher offset.
For seekable binary streams, the remaining size is measured by seeking to the end and restoring the original position. Prefer streams with efficient seeking: transforming streams may read their entire input to find the end. Non-seekable streams and streams whose size cannot be determined use a regular upload without spooling.
Resumable uploads reuse the input unless compression is needed, in which case the compressed payload is spooled in a temporary file (
tempfile.TemporaryFile) on disk before sending. SetClient(spool_dir=<path>)if you wish to customize the directory where the temporary files are created in.
- class objectstore_client.client.Usecase(name: str, compression: Literal['zstd', 'gzip', 'none'] = 'zstd', expiration_policy: TimeToIdle | TimeToLive | None = None, resumable_threshold_bytes: int | Literal['none'] = 268435456)[source]
Bases:
objectAn identifier for a workload in Objectstore, along with defaults to use for all operations within that Usecase.
Usecases need to be statically defined in Objectstore’s configuration server-side. Objectstore can make decisions based on the Usecase. For example, choosing the most suitable storage backend.
Names must be 3–64 ASCII characters from
[A-Za-z0-9_-]. Any permitted character may appear in any position. Names are case-sensitive and are never normalized. The name is read-only after construction.- property name: str
The validated usecase name.
objectstore_client.errors module
- exception objectstore_client.errors.ExpiryExtensionRejected(message: str, status: int | None, response: str | None)[source]
Bases:
RequestErrorThe object’s expiration deadline could not be extended.
The object is non-expiring, lacks required creation metadata, or conflicts with a conditional update. A conflict can result from concurrent deletion, so this does not guarantee that the object still exists.
- exception objectstore_client.errors.ObjectNotFound(message: str, status: int | None, response: str | None)[source]
Bases:
RequestErrorThe object was observed to be absent or expired.
- exception objectstore_client.errors.RequestError(message: str, status: int | None, response: str | None)[source]
Bases:
ExceptionException raised if an API call to Objectstore fails.
- objectstore_client.errors.raise_for_status(response: BaseHTTPResponse, *, error_type: type[RequestError] = <class 'objectstore_client.errors.RequestError'>) None[source]
objectstore_client.metadata module
- class objectstore_client.metadata.Metadata(content_type: 'str | None', compression: 'Compression | None', expiration_policy: 'ExpirationPolicy | None', time_created: 'datetime | None', time_expires: 'datetime | None', origin: 'str | None', filename: 'str | None', size: 'int | None', custom: 'dict[str, str]')[source]
Bases:
object- compression: Literal['zstd', 'gzip'] | Literal['none'] | None
- content_type: str | None
- custom: dict[str, str]
- expiration_policy: TimeToIdle | TimeToLive | None
- filename: str | None
An optional filename associated with this object.
When present, the server includes a Content-Disposition header in GET responses, prompting browsers and download tools to save the file under this name.
- origin: str | None
The origin of the object, typically the IP address of the original source.
This tracks where the payload was originally obtained from (e.g., the IP of a Sentry SDK or CLI).
- size: int | None
The size of the complete stored object in bytes.
This is always the size of the whole object, even when only a range of it was requested. For a compressed object it is the compressed size, matching the bytes on the wire.
This field is computed by the server, it cannot be set by clients.
- time_created: datetime | None
Timestamp indicating when the object was created or the last time it was replaced.
This means that a PUT request to an existing object causes this value to be bumped. This field is computed by the server, it cannot be set by clients.
- time_expires: datetime | None
Timestamp indicating when the object will expire.
When using a Time To Idle expiration policy, this value will reflect the expiration timestamp present prior to the current access to the object.
This field is computed by the server. Use expiration_policy to set the policy on upload, or Session.extend_expiry to extend an existing deadline.
- class objectstore_client.metadata.TimeToIdle(delta: 'timedelta')[source]
Bases:
object- delta: timedelta
- class objectstore_client.metadata.TimeToLive(delta: 'timedelta')[source]
Bases:
object- delta: timedelta
- objectstore_client.metadata.format_expiration(expiration_policy: TimeToIdle | TimeToLive) str[source]
- objectstore_client.metadata.format_timedelta(delta: timedelta) str[source]
Formats a duration in the wire format, such as 400d 1m 30s.
Days are the largest unit, components that are zero are omitted, and a zero duration is written as 0s. Any sub-second remainder is truncated.
- objectstore_client.metadata.itertools_batched(iterable: Iterable[T], n: int, strict: bool = False) Iterator[tuple[T, ...]][source]
Vendored version of itertools.batched, not available in Python 3.11. Batch data from the iterable into tuples of length n. The last batch may be shorter than n. If strict is true, will raise a ValueError if the final batch is shorter than n. Loops over the input iterable and accumulates data into tuples up to size n. The input is consumed lazily, just enough to fill a batch. The result is yielded as soon as the batch is full or when the input iterable is exhausted:
- objectstore_client.metadata.parse_expiration(value: str) TimeToIdle | TimeToLive | None[source]
objectstore_client.metrics module
- class objectstore_client.metrics.MetricsBackend(*args, **kwargs)[source]
Bases:
ProtocolAn abstract class that defines the interface for metrics backends.
- abstractmethod distribution(name: str, value: int | float, tags: Mapping[str, str] | None = None, unit: str | None = None) None[source]
Records a distribution metric.
- class objectstore_client.metrics.NoOpMetricsBackend(*args, **kwargs)[source]
Bases:
MetricsBackendDefault metrics backend that does not record anything.
- distribution(name: str, value: int | float, tags: Mapping[str, str] | None = None, unit: str | None = None) None[source]
Records a distribution metric.
- class objectstore_client.metrics.StorageMetricEmitter(backend: MetricsBackend, operation: str, usecase: str)[source]
Bases:
object
- objectstore_client.metrics.measure_storage_operation(backend: MetricsBackend, operation: str, usecase: str, uncompressed_size: int | None = None, compressed_size: int | None = None, compression: str = 'unknown') Generator[StorageMetricEmitter][source]
Context manager which records the latency of the enclosed storage operation. Can also record the compressed or uncompressed size of an object, the compression ratio, the throughput, and the inverse throughput.
Yields a StorageMetricEmitter because for some operations (GET) the size is not known until the inside of the enclosed block.
objectstore_client.presign module
Utilities for pre-signed URLs.
A pre-signed URL lets a client that owns an Ed25519 keypair hand out a time-limited URL that authorizes a specific request, without the recipient needing an auth token.
Experimental: pre-signed URLs are an experimental feature and this API may change in a future release.
See objectstore-types/src/presign.rs for details on the scheme.
- objectstore_client.presign.build_canonical_form(method: str, encoded_path: str, encoded_query: str) str[source]
Builds the canonical form of a request to be signed.
encoded_pathandencoded_querymust already be percent-encoded as they will appear on the wire (seeobjectstore_client.utils.encode_path()andobjectstore_client.utils.encode_query()).
objectstore_client.scope module
objectstore_client.tracing module
- objectstore_client.tracing.storage_span(operation: str, usecase: Usecase, scope: Scope, **attributes: SpanAttributeValue | None) Generator[StreamedSpan][source]
Starts a span for a single Objectstore operation.
operationis the same literal already passed tomeasure_storage_operation, so the span’sop(objectstore.<operation>), its name, and thestorage.<operation>.*metrics all derive from one source. Keyword arguments becomeobjectstore.<key>span attributes, withNonevalues dropped so call sites can pass optionals inline; attributes that are only known after the request completes can be set on the yielded span instead.
objectstore_client.utils module
- objectstore_client.utils.decode_header_value(value: str) str[source]
Decodes a percent-encoded HTTP header value into its logical string.
Inverse of
encode_header_value(). Values without escape sequences are returned unchanged, so headers written before the encoding existed still read back as-is.
- objectstore_client.utils.encode_header_value(value: str) str[source]
Percent-encodes a free-form metadata string for an HTTP header value.
HTTP header values carry only visible ASCII, so anything outside that range has to be escaped to survive the transport (see
_HEADER_VALUE_SAFE). This is the inverse ofdecode_header_value(); only the transport representation is escaped, never the logical string held inMetadata.
- objectstore_client.utils.encode_path(path: str) str[source]
Percent-encodes a request path as it will appear on the wire.
Leaves the RFC 3986 unreserved set, sub-delims, and
:@/unescaped (see_PATH_SAFE); everything else is percent-encoded. This treats the input as a literal string: a literal%becomes%25.
- objectstore_client.utils.encode_query(query: str) str[source]
Percent-encodes a query string as it will appear on the wire.
Same set as
encode_path(), additionally leaving?unescaped (see_QUERY_SAFE).
- objectstore_client.utils.guess_mime_type(contents: bytes | IO[bytes]) str | None[source]
Guesses the MIME type from the given contents.
Reads up to 261 bytes from the beginning of the content to determine the MIME type using file header signatures.
To guess the MIME type from a filename, use mimetypes.guess_type, which is part of the standard library.
- objectstore_client.utils.parse_accept_encoding(header: str) list[str][source]
Parse an Accept-Encoding header value for use in objectstore GET requests.
Returns a list of encoding names, normalized to lowercase and stripped of q-values, per RFC 7231. Encodings explicitly rejected via
q=0are excluded.Note:
identity;q=0is not supported and will be silently ignored (i.e., treated as acceptable), since objectstore always serves an identity fallback.