cs3client


Namecs3client JSON
Version 1.3.0 PyPI version JSON
download
home_pagehttps://github.com/cs3org/cs3-python-client
SummaryCS3 client for Python
upload_time2025-08-29 15:16:28
maintainerNone
docs_urlNone
authorRasmus Welander, Diogo Castro, Giuseppe Lo Presti
requires_pythonNone
licenseApache 2.0
keywords
VCS
bugtrack_url
requirements grpcio grpcio-tools pyOpenSSL requests cs3apis PyJWT protobuf cryptography sphinx
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # CS3Client

`CS3Client` is a Python client for interacting with the CS3 (Cloud Sync&Share Storage) [APIs](https://github.com/cs3org/cs3apis). It allows users to seamlessly communicate with cloud storage services that support CS3 protocols, enabling file management, data transfer, and other cloud-based operations.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Examples](#examples)
- [Documentation](#documentation)
- [License](#license)


## Features

- Simple and easy-to-use API client for CS3 services.
- Support for common file operations (read, write, delete, rename, ...).
- Support for common lock operations (set lock, get lock, unlock, ...).
- Support for common share operations (create share, update share, delete share, ...).
- Support for common user operations (get user, find users, get user groups, ...).
- Support for restoring files through checkpoints (restore file version, list checkpoints).
- Support for applications (open in app, list app providers).
- Authentication and authorization handling.
- Cross-platform compatibility.
- Detailed error handling and logging.

## Installation

To install `cs3client`, you need to have Python 3.7+ installed. You can install the package via `pip`:

```bash
pip install cs3client
```
Alternatively, you can clone this repository and install manually:
```bash
git clone git@github.com:cs3org/cs3-python-client.git
cd cs3-python-client
pip install . 
```


## Configuration

`CS3Client` can be configured by passing specific parameters when initializing the client through a ConfigParser instance.

### Parameters:

#### Required
- `host`

#### Optional (parameter - default)
- `chunk_size` - 4194384
- `grpc_timeout` - 10
- `http_timeout` - 10
- `tus_enabled` - False
- `ssl_enabled` - False
- `ssl_client_cert` - None
- `ssl_client_key` - None
- `ssl_ca_cert` - None
- `auth_client_id` - None
- `auth_login_type` - "basic"
- `lock_by_setting_attr` - False
- `lock_not_impl` - False
- `lock_expiration` - 1800

#### Example configuration
```yaml
[cs3client]

# Required
host = localhost:19000
# Optional, defaults to 4194304
chunk_size = 4194304
# Optional, defaults to 10
grpc_timeout = 10
# Optional, defaults to 10
http_timeout = 10

# Optional, defaults to True
tus_enabled = False

# Optional, defaults to True
ssl_enabled = False
# Optional, defaults to True
ssl_verify = False
# Optional, defaults to an empty string
ssl_client_cert = test_client_cert
# Optional, defaults to an empty string
ssl_client_key = test_client_key
# Optional, defaults to an empty string
ssl_ca_cert = test_ca_cert

# Optinal, defaults to an empty string
auth_client_id = einstein
# Optional (can also be set when instansiating the class)
auth_client_secret = relativity
# Optional, defaults to basic
auth_login_type = basic

# Optional, defaults to False
lock_by_setting_attr = False
# Optional, defaults to False
lock_not_impl = False
# Optional, defaults to 1800
lock_expiration = 1800


```

## Usage

To use `cs3client`, you first need to import and configure it. Here's a simple example of how to set up and start using the client. For configuration see [Configuration](#configuration). For more in depth examples see `cs3-python-client/examples/`. 

### Initilization and Authentication
```python
import logging
import configparser
from cs3client.cs3client import CS3Client
from cs3client.auth import Auth

config = configparser.ConfigParser()
with open("default.conf") as fdef:
    config.read_file(fdef)
log = logging.getLogger(__name__)

client = CS3Client(config, "cs3client", log)
auth = Auth(client)
# Set the client id (can also be set in the config)
auth.set_client_id("<your_client_id_here>")
# Set client secret (can also be set in config)
auth.set_client_secret("<your_client_secret_here>")
# Checks if token is expired if not return ('x-access-token', <token>)
# if expired, request a new token from reva
auth_token = auth.get_token()

# OR if you already have a reva token
# Checks if token is expired if not return (x-access-token', <token>)
# if expired, throws an AuthenticationException (so you can refresh your reva token)
token = "<your_reva_token>"
auth_token = Auth.check_token(token)

```

### File Example
```python
# mkdir
directory_resource = Resource(abs_path=f"/eos/user/r/rwelande/test_directory")
res = client.file.make_dir(auth.get_token(), directory_resource)

# touchfile
touch_resource = Resource(abs_path="/eos/user/r/rwelande/touch_file.txt")
res = client.file.touch_file(auth.get_token(), touch_resource)

# setxattr
resource = Resource(abs_path="/eos/user/r/rwelande/text_file.txt")
res = client.file.set_xattr(auth.get_token(), resource, "iop.wopi.lastwritetime", str(1720696124))

# rmxattr
res = client.file.remove_xattr(auth.get_token(), resource, "iop.wopi.lastwritetime")

# stat
res = client.file.stat(auth.get_token(), resource)

# removefile
res = client.file.remove_file(auth.get_token(), touch_resource)

# rename
rename_resource = Resource(abs_path="/eos/user/r/rwelande/rename_file.txt")
res = client.file.rename_file(auth.get_token(), resource, rename_resource)

# writefile
content = b"Hello World"
size = len(content)
res = client.file.write_file(auth.get_token(), rename_resource, content, size)

# listdir
list_directory_resource = Resource(abs_path="/eos/user/r/rwelande")
res = client.file.list_dir(auth.get_token(), list_directory_resource)


# readfile
file_res = client.file.read_file(auth.get_token(), rename_resource)
```
### Lock Example
```python

WEBDAV_LOCK_PREFIX = 'opaquelocktoken:797356a8-0500-4ceb-a8a0-c94c8cde7eba'


def encode_lock(lock):
    '''Generates the lock payload for the storage given the raw metadata'''
    if lock:
        return WEBDAV_LOCK_PREFIX + ' ' + b64encode(lock.encode()).decode()
    return None

resource = Resource(abs_path="/eos/user/r/rwelande/lock_test.txt")

# Set lock
client.file.set_lock(auth_token, resource, app_name="a", lock_id=encode_lock("some_lock"))

# Get lock
res = client.file.get_lock(auth_token, resource)
if res is not None:
    lock_id = res["lock_id"]
    print(res)

# Unlock
res = client.file.unlock(auth_token, resource, app_name="a", lock_id=lock_id)

# Refresh lock
client.file.set_lock(auth_token, resource, app_name="a", lock_id=encode_lock("some_lock"))
res = client.file.refresh_lock(
    auth_token, resource, app_name="a", lock_id=encode_lock("new_lock"), existing_lock_id=lock_id
)

if res is not None:
    print(res)

res = client.file.get_lock(auth_token, resource)
if res is not None:
    print(res)

```

### Share Example
```python
# Create share #
resource = Resource(abs_path="/eos/user/r/<some_username>/text.txt")
resource_info = client.file.stat(auth.get_token(), resource)
user = client.user.get_user_by_claim("username", "<some_username>")
res = client.share.create_share(auth.get_token(), resource_info, user.id.opaque_id, user.id.idp, "EDITOR", "USER")

# List existing shares #
filter_list = []
filter = client.share.create_share_filter(resource_id=resource_info.id, filter_type="TYPE_RESOURCE_ID")
filter_list.append(filter)
filter = client.share.create_share_filter(share_state="SHARE_STATE_PENDING", filter_type="TYPE_STATE")
filter_list.append(filter)
res, _ = client.share.list_existing_shares(auth.get_token(), )

# Get share #
share_id = "58"
res = client.share.get_share(auth.get_token(), opaque_id=share_id)

# update share #
res = client.share.update_share(auth.get_token(), opaque_id=share_id, role="VIEWER")

# remove share #
res = client.share.remove_share(auth.get_token(), opaque_id=share_id)

# List existing received shares #
filter_list = []
filter = client.share.create_share_filter(share_state="SHARE_STATE_ACCEPTED", filter_type="TYPE_STATE")
filter_list.append(filter)
res, _ = client.share.list_received_existing_shares(auth.get_token())

# get received share #
received_share = client.share.get_received_share(auth.get_token(), opaque_id=share_id)

# update recieved share #
res = client.share.update_received_share(auth.get_token(), received_share=received_share, state="SHARE_STATE_ACCEPTED")

# create public share #
res = client.share.create_public_share(auth.get_token(), resource_info, role="VIEWER")

# list existing public shares #
filter_list = []
filter = client.share.create_public_share_filter(resource_id=resource_info.id, filter_type="TYPE_RESOURCE_ID")
filter_list.append(filter)
res, _ = client.share.list_existing_public_shares(filter_list=filter_list)

res = client.share.get_public_share(auth.get_token(), opaque_id=share_id, sign=True)
# OR token = "<token>"
# res = client.share.get_public_share(token=token, sign=True)

# update public share #
res = client.share.update_public_share(auth.get_token(), type="TYPE_PASSWORD", token=token, role="VIEWER", password="hello")

# remove public share #
res = client.share.remove_public_share(auth.get_token(), token=token)

```

### User Example
```python
# find_user
res = client.user.find_users(auth.get_token(), "rwel")

# get_user
res = client.user.get_user("https://auth.cern.ch/auth/realms/cern", "asdoiqwe")

# get_user_groups
res = client.user.get_user_groups("https://auth.cern.ch/auth/realms/cern", "rwelande")

# get_user_by_claim (mail)
res = client.user.get_user_by_claim("mail", "rasmus.oscar.welander@cern.ch")

# get_user_by_claim (username)
res = client.user.get_user_by_claim("username", "rwelande")

```

### App Example
```python
# list_app_providers
res = client.app.list_app_providers(auth.get_token())

# open_in_app
resource = Resource(abs_path="/eos/user/r/rwelande/collabora.odt")
res = client.app.open_in_app(auth.get_token(), resource)
```

### Checkpoint Example
```python
# list file versions
resource = Resource(abs_path="/eos/user/r/rwelande/test.md")
res = client.checkpoint.list_file_versions(auth.get_token(), resource)

# restore file version
res = client.checkpoint.restore_file_version(auth.get_token(), resource, "1722936250.0569fa2f")
```

## Documentation
The documentation can be generated using sphinx

```bash
pip install sphinx
cd docs
make html
```

## Unit tests

```bash
pytest --cov-report term --cov=serc tests/
```

## License

This project is licensed under the Apache 2.0 License. See the LICENSE file for more details.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/cs3org/cs3-python-client",
    "name": "cs3client",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": null,
    "author": "Rasmus Welander, Diogo Castro, Giuseppe Lo Presti",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/75/d5/be0ecb159f8275a21c613fbb17064ee68547fc551101a4ee910997b8ae0b/cs3client-1.3.0.tar.gz",
    "platform": null,
    "description": "# CS3Client\n\n`CS3Client` is a Python client for interacting with the CS3 (Cloud Sync&Share Storage) [APIs](https://github.com/cs3org/cs3apis). It allows users to seamlessly communicate with cloud storage services that support CS3 protocols, enabling file management, data transfer, and other cloud-based operations.\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Configuration](#configuration)\n- [Examples](#examples)\n- [Documentation](#documentation)\n- [License](#license)\n\n\n## Features\n\n- Simple and easy-to-use API client for CS3 services.\n- Support for common file operations (read, write, delete, rename, ...).\n- Support for common lock operations (set lock, get lock, unlock, ...).\n- Support for common share operations (create share, update share, delete share, ...).\n- Support for common user operations (get user, find users, get user groups, ...).\n- Support for restoring files through checkpoints (restore file version, list checkpoints).\n- Support for applications (open in app, list app providers).\n- Authentication and authorization handling.\n- Cross-platform compatibility.\n- Detailed error handling and logging.\n\n## Installation\n\nTo install `cs3client`, you need to have Python 3.7+ installed. You can install the package via `pip`:\n\n```bash\npip install cs3client\n```\nAlternatively, you can clone this repository and install manually:\n```bash\ngit clone git@github.com:cs3org/cs3-python-client.git\ncd cs3-python-client\npip install . \n```\n\n\n## Configuration\n\n`CS3Client` can be configured by passing specific parameters when initializing the client through a ConfigParser instance.\n\n### Parameters:\n\n#### Required\n- `host`\n\n#### Optional (parameter - default)\n- `chunk_size` - 4194384\n- `grpc_timeout` - 10\n- `http_timeout` - 10\n- `tus_enabled` - False\n- `ssl_enabled` - False\n- `ssl_client_cert` - None\n- `ssl_client_key` - None\n- `ssl_ca_cert` - None\n- `auth_client_id` - None\n- `auth_login_type` - \"basic\"\n- `lock_by_setting_attr` - False\n- `lock_not_impl` - False\n- `lock_expiration` - 1800\n\n#### Example configuration\n```yaml\n[cs3client]\n\n# Required\nhost = localhost:19000\n# Optional, defaults to 4194304\nchunk_size = 4194304\n# Optional, defaults to 10\ngrpc_timeout = 10\n# Optional, defaults to 10\nhttp_timeout = 10\n\n# Optional, defaults to True\ntus_enabled = False\n\n# Optional, defaults to True\nssl_enabled = False\n# Optional, defaults to True\nssl_verify = False\n# Optional, defaults to an empty string\nssl_client_cert = test_client_cert\n# Optional, defaults to an empty string\nssl_client_key = test_client_key\n# Optional, defaults to an empty string\nssl_ca_cert = test_ca_cert\n\n# Optinal, defaults to an empty string\nauth_client_id = einstein\n# Optional (can also be set when instansiating the class)\nauth_client_secret = relativity\n# Optional, defaults to basic\nauth_login_type = basic\n\n# Optional, defaults to False\nlock_by_setting_attr = False\n# Optional, defaults to False\nlock_not_impl = False\n# Optional, defaults to 1800\nlock_expiration = 1800\n\n\n```\n\n## Usage\n\nTo use `cs3client`, you first need to import and configure it. Here's a simple example of how to set up and start using the client. For configuration see [Configuration](#configuration). For more in depth examples see `cs3-python-client/examples/`. \n\n### Initilization and Authentication\n```python\nimport logging\nimport configparser\nfrom cs3client.cs3client import CS3Client\nfrom cs3client.auth import Auth\n\nconfig = configparser.ConfigParser()\nwith open(\"default.conf\") as fdef:\n    config.read_file(fdef)\nlog = logging.getLogger(__name__)\n\nclient = CS3Client(config, \"cs3client\", log)\nauth = Auth(client)\n# Set the client id (can also be set in the config)\nauth.set_client_id(\"<your_client_id_here>\")\n# Set client secret (can also be set in config)\nauth.set_client_secret(\"<your_client_secret_here>\")\n# Checks if token is expired if not return ('x-access-token', <token>)\n# if expired, request a new token from reva\nauth_token = auth.get_token()\n\n# OR if you already have a reva token\n# Checks if token is expired if not return (x-access-token', <token>)\n# if expired, throws an AuthenticationException (so you can refresh your reva token)\ntoken = \"<your_reva_token>\"\nauth_token = Auth.check_token(token)\n\n```\n\n### File Example\n```python\n# mkdir\ndirectory_resource = Resource(abs_path=f\"/eos/user/r/rwelande/test_directory\")\nres = client.file.make_dir(auth.get_token(), directory_resource)\n\n# touchfile\ntouch_resource = Resource(abs_path=\"/eos/user/r/rwelande/touch_file.txt\")\nres = client.file.touch_file(auth.get_token(), touch_resource)\n\n# setxattr\nresource = Resource(abs_path=\"/eos/user/r/rwelande/text_file.txt\")\nres = client.file.set_xattr(auth.get_token(), resource, \"iop.wopi.lastwritetime\", str(1720696124))\n\n# rmxattr\nres = client.file.remove_xattr(auth.get_token(), resource, \"iop.wopi.lastwritetime\")\n\n# stat\nres = client.file.stat(auth.get_token(), resource)\n\n# removefile\nres = client.file.remove_file(auth.get_token(), touch_resource)\n\n# rename\nrename_resource = Resource(abs_path=\"/eos/user/r/rwelande/rename_file.txt\")\nres = client.file.rename_file(auth.get_token(), resource, rename_resource)\n\n# writefile\ncontent = b\"Hello World\"\nsize = len(content)\nres = client.file.write_file(auth.get_token(), rename_resource, content, size)\n\n# listdir\nlist_directory_resource = Resource(abs_path=\"/eos/user/r/rwelande\")\nres = client.file.list_dir(auth.get_token(), list_directory_resource)\n\n\n# readfile\nfile_res = client.file.read_file(auth.get_token(), rename_resource)\n```\n### Lock Example\n```python\n\nWEBDAV_LOCK_PREFIX = 'opaquelocktoken:797356a8-0500-4ceb-a8a0-c94c8cde7eba'\n\n\ndef encode_lock(lock):\n    '''Generates the lock payload for the storage given the raw metadata'''\n    if lock:\n        return WEBDAV_LOCK_PREFIX + ' ' + b64encode(lock.encode()).decode()\n    return None\n\nresource = Resource(abs_path=\"/eos/user/r/rwelande/lock_test.txt\")\n\n# Set lock\nclient.file.set_lock(auth_token, resource, app_name=\"a\", lock_id=encode_lock(\"some_lock\"))\n\n# Get lock\nres = client.file.get_lock(auth_token, resource)\nif res is not None:\n    lock_id = res[\"lock_id\"]\n    print(res)\n\n# Unlock\nres = client.file.unlock(auth_token, resource, app_name=\"a\", lock_id=lock_id)\n\n# Refresh lock\nclient.file.set_lock(auth_token, resource, app_name=\"a\", lock_id=encode_lock(\"some_lock\"))\nres = client.file.refresh_lock(\n    auth_token, resource, app_name=\"a\", lock_id=encode_lock(\"new_lock\"), existing_lock_id=lock_id\n)\n\nif res is not None:\n    print(res)\n\nres = client.file.get_lock(auth_token, resource)\nif res is not None:\n    print(res)\n\n```\n\n### Share Example\n```python\n# Create share #\nresource = Resource(abs_path=\"/eos/user/r/<some_username>/text.txt\")\nresource_info = client.file.stat(auth.get_token(), resource)\nuser = client.user.get_user_by_claim(\"username\", \"<some_username>\")\nres = client.share.create_share(auth.get_token(), resource_info, user.id.opaque_id, user.id.idp, \"EDITOR\", \"USER\")\n\n# List existing shares #\nfilter_list = []\nfilter = client.share.create_share_filter(resource_id=resource_info.id, filter_type=\"TYPE_RESOURCE_ID\")\nfilter_list.append(filter)\nfilter = client.share.create_share_filter(share_state=\"SHARE_STATE_PENDING\", filter_type=\"TYPE_STATE\")\nfilter_list.append(filter)\nres, _ = client.share.list_existing_shares(auth.get_token(), )\n\n# Get share #\nshare_id = \"58\"\nres = client.share.get_share(auth.get_token(), opaque_id=share_id)\n\n# update share #\nres = client.share.update_share(auth.get_token(), opaque_id=share_id, role=\"VIEWER\")\n\n# remove share #\nres = client.share.remove_share(auth.get_token(), opaque_id=share_id)\n\n# List existing received shares #\nfilter_list = []\nfilter = client.share.create_share_filter(share_state=\"SHARE_STATE_ACCEPTED\", filter_type=\"TYPE_STATE\")\nfilter_list.append(filter)\nres, _ = client.share.list_received_existing_shares(auth.get_token())\n\n# get received share #\nreceived_share = client.share.get_received_share(auth.get_token(), opaque_id=share_id)\n\n# update recieved share #\nres = client.share.update_received_share(auth.get_token(), received_share=received_share, state=\"SHARE_STATE_ACCEPTED\")\n\n# create public share #\nres = client.share.create_public_share(auth.get_token(), resource_info, role=\"VIEWER\")\n\n# list existing public shares #\nfilter_list = []\nfilter = client.share.create_public_share_filter(resource_id=resource_info.id, filter_type=\"TYPE_RESOURCE_ID\")\nfilter_list.append(filter)\nres, _ = client.share.list_existing_public_shares(filter_list=filter_list)\n\nres = client.share.get_public_share(auth.get_token(), opaque_id=share_id, sign=True)\n# OR token = \"<token>\"\n# res = client.share.get_public_share(token=token, sign=True)\n\n# update public share #\nres = client.share.update_public_share(auth.get_token(), type=\"TYPE_PASSWORD\", token=token, role=\"VIEWER\", password=\"hello\")\n\n# remove public share #\nres = client.share.remove_public_share(auth.get_token(), token=token)\n\n```\n\n### User Example\n```python\n# find_user\nres = client.user.find_users(auth.get_token(), \"rwel\")\n\n# get_user\nres = client.user.get_user(\"https://auth.cern.ch/auth/realms/cern\", \"asdoiqwe\")\n\n# get_user_groups\nres = client.user.get_user_groups(\"https://auth.cern.ch/auth/realms/cern\", \"rwelande\")\n\n# get_user_by_claim (mail)\nres = client.user.get_user_by_claim(\"mail\", \"rasmus.oscar.welander@cern.ch\")\n\n# get_user_by_claim (username)\nres = client.user.get_user_by_claim(\"username\", \"rwelande\")\n\n```\n\n### App Example\n```python\n# list_app_providers\nres = client.app.list_app_providers(auth.get_token())\n\n# open_in_app\nresource = Resource(abs_path=\"/eos/user/r/rwelande/collabora.odt\")\nres = client.app.open_in_app(auth.get_token(), resource)\n```\n\n### Checkpoint Example\n```python\n# list file versions\nresource = Resource(abs_path=\"/eos/user/r/rwelande/test.md\")\nres = client.checkpoint.list_file_versions(auth.get_token(), resource)\n\n# restore file version\nres = client.checkpoint.restore_file_version(auth.get_token(), resource, \"1722936250.0569fa2f\")\n```\n\n## Documentation\nThe documentation can be generated using sphinx\n\n```bash\npip install sphinx\ncd docs\nmake html\n```\n\n## Unit tests\n\n```bash\npytest --cov-report term --cov=serc tests/\n```\n\n## License\n\nThis project is licensed under the Apache 2.0 License. See the LICENSE file for more details.\n",
    "bugtrack_url": null,
    "license": "Apache 2.0",
    "summary": "CS3 client for Python",
    "version": "1.3.0",
    "project_urls": {
        "Homepage": "https://github.com/cs3org/cs3-python-client"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "521efd35dab0b0af893e7af296e4b346226cf2e5fbd60e34819c211635e7704d",
                "md5": "cf8c8a1af47c581d2750ad742ad11f33",
                "sha256": "da6aff6d1ce91b54aa8b79db543254132930f6c9a4720e60be5857cbd56f035e"
            },
            "downloads": -1,
            "filename": "cs3client-1.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "cf8c8a1af47c581d2750ad742ad11f33",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 31443,
            "upload_time": "2025-08-29T15:16:26",
            "upload_time_iso_8601": "2025-08-29T15:16:26.981585Z",
            "url": "https://files.pythonhosted.org/packages/52/1e/fd35dab0b0af893e7af296e4b346226cf2e5fbd60e34819c211635e7704d/cs3client-1.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "75d5be0ecb159f8275a21c613fbb17064ee68547fc551101a4ee910997b8ae0b",
                "md5": "ac8755cd1cc7012020543aadc41d2c6b",
                "sha256": "bd84ba8afbe2a0cfe08a1a61cf602ea15c6fd3429a667a6e8c5a01aa7dd44df8"
            },
            "downloads": -1,
            "filename": "cs3client-1.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "ac8755cd1cc7012020543aadc41d2c6b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 34427,
            "upload_time": "2025-08-29T15:16:28",
            "upload_time_iso_8601": "2025-08-29T15:16:28.069618Z",
            "url": "https://files.pythonhosted.org/packages/75/d5/be0ecb159f8275a21c613fbb17064ee68547fc551101a4ee910997b8ae0b/cs3client-1.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-29 15:16:28",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "cs3org",
    "github_project": "cs3-python-client",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "grpcio",
            "specs": [
                [
                    ">=",
                    "1.47.0"
                ]
            ]
        },
        {
            "name": "grpcio-tools",
            "specs": [
                [
                    ">=",
                    "1.47.0"
                ]
            ]
        },
        {
            "name": "pyOpenSSL",
            "specs": []
        },
        {
            "name": "requests",
            "specs": []
        },
        {
            "name": "cs3apis",
            "specs": [
                [
                    ">=",
                    "0.1.dev101"
                ]
            ]
        },
        {
            "name": "PyJWT",
            "specs": []
        },
        {
            "name": "protobuf",
            "specs": []
        },
        {
            "name": "cryptography",
            "specs": []
        },
        {
            "name": "sphinx",
            "specs": []
        }
    ],
    "lcname": "cs3client"
}
        
Elapsed time: 2.99086s