dhclients 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ Metadata-Version: 2.4
2
+ Name: dhclients
3
+ Version: 0.1.0
4
+ Summary: DARIAH Python client library for repository.dariah.de access.
5
+ Author: Stefan Hynek, Ubbo Veentjer
6
+ Author-email: Stefan Hynek <hynek@sub.uni-goettingen.de>, Ubbo Veentjer <veentjer@sub.uni-goettingen.de>
7
+ License-Expression: LGPL-3.0-or-later
8
+ Requires-Dist: requests>=2.34.2
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+
12
+ <!--
13
+ SPDX-FileCopyrightText: 2026 Georg-August-Universität Göttingen
14
+
15
+ SPDX-License-Identifier: CC0-1.0
16
+ -->
17
+
18
+ # DHRep Python clients
19
+
20
+ The DHRep Python clients provide access to the [DARIAH Repository](https://repository.de.dariah.eu/)
21
+ services [API](https://doc.repository.de.dariah.eu/).
22
+
23
+ ## Installation and Usage
24
+
25
+ ```sh
26
+ pip install dhclients
27
+ ```
28
+
29
+ ## Development
30
+
31
+ ### Pre-Commit
32
+
33
+ install pre-commmit
34
+
35
+ ```sh
36
+ uv tool install pre-commit --with pre-commit-uv
37
+ uv run pre-commit install
38
+ ```
39
+
40
+
41
+ ### Running integration tests
42
+
43
+ We run integration tests against the dev repo (trep)
44
+
45
+ Get your auth token by log in on publikator: https://trep.de.dariah.eu/publikator/mainView
46
+ click on "Show storage token", copy your token and add to env
47
+
48
+ ```sh
49
+ DHREP_ENDPOINT=development DARIAH_STORAGE_TOKEN=my-secret-token-here uv run pytest --integration
50
+ ```
@@ -0,0 +1,39 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Georg-August-Universität Göttingen
3
+
4
+ SPDX-License-Identifier: CC0-1.0
5
+ -->
6
+
7
+ # DHRep Python clients
8
+
9
+ The DHRep Python clients provide access to the [DARIAH Repository](https://repository.de.dariah.eu/)
10
+ services [API](https://doc.repository.de.dariah.eu/).
11
+
12
+ ## Installation and Usage
13
+
14
+ ```sh
15
+ pip install dhclients
16
+ ```
17
+
18
+ ## Development
19
+
20
+ ### Pre-Commit
21
+
22
+ install pre-commmit
23
+
24
+ ```sh
25
+ uv tool install pre-commit --with pre-commit-uv
26
+ uv run pre-commit install
27
+ ```
28
+
29
+
30
+ ### Running integration tests
31
+
32
+ We run integration tests against the dev repo (trep)
33
+
34
+ Get your auth token by log in on publikator: https://trep.de.dariah.eu/publikator/mainView
35
+ click on "Show storage token", copy your token and add to env
36
+
37
+ ```sh
38
+ DHREP_ENDPOINT=development DARIAH_STORAGE_TOKEN=my-secret-token-here uv run pytest --integration
39
+ ```
@@ -0,0 +1,65 @@
1
+ # SPDX-FileCopyrightText: 2026 Georg-August-Universität Göttingen
2
+ #
3
+ # SPDX-License-Identifier: CC0-1.0
4
+
5
+
6
+ [project]
7
+ name = "dhclients"
8
+ version = "0.1.0"
9
+ description = "DARIAH Python client library for repository.dariah.de access."
10
+ readme = "README.md"
11
+ license = "LGPL-3.0-or-later"
12
+ authors = [
13
+ { name = "Stefan Hynek", email = "hynek@sub.uni-goettingen.de" },
14
+ { name = "Ubbo Veentjer", email = "veentjer@sub.uni-goettingen.de" }
15
+ ]
16
+ requires-python = ">=3.10"
17
+ dependencies = [
18
+ "requests>=2.34.2",
19
+ ]
20
+
21
+ [dependency-groups]
22
+ dev = [
23
+ "coverage>=7.14.3",
24
+ "pytest>=9.0.3",
25
+ "python-semantic-release>=10.5.3",
26
+ "requests-mock>=1.12.1",
27
+ "ruff>=0.15.14",
28
+ ]
29
+
30
+ [build-system]
31
+ requires = ["uv_build>=0.9.13,<0.10.0"]
32
+ build-backend = "uv_build"
33
+
34
+ [tool.semantic_release]
35
+ build_command = """
36
+ uv lock --upgrade-package "$PACKAGE_NAME"
37
+ uv build
38
+ """
39
+ version_toml = ["pyproject.toml:project.version"]
40
+ version_variables = [
41
+ "src/dhclients/__init__.py:__version__",
42
+ ]
43
+
44
+ allow_zero_version = true
45
+ tag_format = "v{version}"
46
+
47
+ [tool.semantic_release.remote]
48
+ type = "gitlab"
49
+
50
+ [tool.ruff]
51
+ exclude = [
52
+ "build",
53
+ "venv",
54
+ ]
55
+
56
+ line-length = 100
57
+
58
+ [tool.ruff.format]
59
+ quote-style = "single"
60
+
61
+ [tool.pydoclint]
62
+ style = 'google'
63
+ skip-checking-raises = true
64
+ allow-init-docstring = true
65
+ check-return-types = false # TODO: check auth.py zeep return types
@@ -0,0 +1,5 @@
1
+ # SPDX-FileCopyrightText: 2026 Georg-August-Universität Göttingen
2
+ #
3
+ # SPDX-License-Identifier: LGPL-3.0-or-later
4
+
5
+ __version__ = '0.1.0'
@@ -0,0 +1,85 @@
1
+ # SPDX-FileCopyrightText: 2026 Georg-August-Universität Göttingen
2
+ #
3
+ # SPDX-License-Identifier: LGPL-3.0-or-later
4
+
5
+ import logging
6
+ from typing import Optional
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ DEFAULT_TIMEOUT: float = 120.00
11
+
12
+ ENDPOINTS: dict = {
13
+ 'production': {
14
+ 'storage': 'https://cdstar.de.dariah.eu/dariah',
15
+ 'publish': 'https://repository.de.dariah.eu/1.0/dhpublish',
16
+ },
17
+ 'development': {
18
+ 'storage': 'https://cdstar.de.dariah.eu/test/dariah',
19
+ 'publish': 'https://trep.de.dariah.eu/1.0/dhpublish',
20
+ },
21
+ 'test': {
22
+ 'storage': 'https://cdstar.de.dariah.eu/test/dariah',
23
+ 'publish': 'https://dhrepworkshop.de.dariah.eu/1.0/dhpublish',
24
+ },
25
+ }
26
+
27
+
28
+ class DhrepConfig:
29
+ def __init__(
30
+ self,
31
+ endpoint: Optional[str] = 'production',
32
+ storage_override: Optional[str] = '',
33
+ publish_override: Optional[str] = '',
34
+ ) -> None:
35
+ """DHrep Service Endpoint Configuration.
36
+
37
+ Overwrite the endpoint string to connect to other servers, e.g.:
38
+ * Test-Server: 'test'
39
+ * Development-Server: 'development'
40
+
41
+ It is also possible to override the storage url and the publish url separately
42
+
43
+ Args:
44
+ endpoint (Optional[str]): DHrep server. Defaults to 'production'.
45
+ storage_override (Optional[str]): overide storage url from ENDPOINTS dict. defaults to ''
46
+ publish_override (Optional[str]): overide publish url from ENDPOINTS dict. defaults to ''
47
+ """
48
+
49
+ if endpoint not in ENDPOINTS:
50
+ raise ConfigException('Endpoint config invalid')
51
+
52
+ if not isinstance(storage_override, str) or storage_override == '':
53
+ self._storage = ENDPOINTS[endpoint]['storage']
54
+ else:
55
+ if storage_override.endswith('/'):
56
+ storage_override = storage_override[:-1]
57
+ logger.info('trailing slash in storage location detected and removed')
58
+ self._storage = storage_override
59
+
60
+ if not isinstance(publish_override, str) or publish_override == '':
61
+ self._publish = ENDPOINTS[endpoint]['publish']
62
+ else:
63
+ if publish_override.endswith('/'):
64
+ publish_override = publish_override[:-1]
65
+ logger.info('trailing slash in publish location detected and removed')
66
+ self._publish = publish_override
67
+
68
+ logger.info(
69
+ 'storage endpoint set to: %s / publish endpoint set to: %s',
70
+ self._storage,
71
+ self._publish,
72
+ )
73
+ self._http_timeout = DEFAULT_TIMEOUT
74
+
75
+ @property
76
+ def storage(self) -> str:
77
+ return self._storage
78
+
79
+ @property
80
+ def publish(self) -> str:
81
+ return self._publish
82
+
83
+
84
+ class ConfigException(Exception):
85
+ """Thrown in case of problems with the storage"""
File without changes
@@ -0,0 +1,149 @@
1
+ # SPDX-FileCopyrightText: 2026 Georg-August-Universität Göttingen
2
+ #
3
+ # SPDX-License-Identifier: LGPL-3.0-or-later
4
+
5
+ """Connection to DARIAH-DE storage
6
+
7
+ Storage API docs: http://hdl.handle.net/11858/00-1734-0000-0009-FEA1-D
8
+ """
9
+
10
+ from typing import IO
11
+
12
+ import requests
13
+ from requests.models import Response
14
+
15
+ from dhclients.config import DhrepConfig
16
+
17
+
18
+ class Storage:
19
+ """
20
+ Methods to create and update data objects in DARIAH-DE storage
21
+ """
22
+
23
+ def __init__(self, config: DhrepConfig = DhrepConfig()):
24
+ self._storage_url = config.storage
25
+ self._config = config
26
+ # reuse tcp connections: https://requests.readthedocs.io/en/latest/user/advanced/#session-objects
27
+ self._requests = requests.Session()
28
+
29
+ def create(self, token: str, content: IO[bytes] | str, content_type: str) -> str:
30
+ """Create a new object in dariahstorage.
31
+
32
+ Args:
33
+ token (str): authentication token
34
+ content (IO[bytes] | str): data to store
35
+ content_type (str): content type
36
+
37
+ Raises:
38
+ Exception: if response from storage had no 201 status code
39
+
40
+ Returns:
41
+ str: the storage id of created object
42
+ """
43
+
44
+ response = self._requests.post(
45
+ self._storage_url + '/',
46
+ headers={'Authorization': 'bearer ' + token, 'Content-type': content_type},
47
+ data=content,
48
+ )
49
+
50
+ if response.status_code != 201:
51
+ raise StorageException(
52
+ 'Error creating new cdstar object: '
53
+ + response.text
54
+ + ' - '
55
+ + str(response.status_code)
56
+ )
57
+
58
+ storage_id = response.headers['location'].rsplit('/', 1)[-1]
59
+ return storage_id
60
+
61
+ def read(self, token: str, storage_id: str) -> Response:
62
+ """Get an object from dariahstorage.
63
+
64
+ Args:
65
+ token (str): authentication token
66
+ storage_id (str): the storage id
67
+
68
+ Raises:
69
+ Exception: if response from storage had no 200 status code
70
+
71
+ Returns:
72
+ Response: HTTP response from service
73
+ """
74
+ response = self._requests.get(
75
+ self._storage_url + '/' + storage_id,
76
+ headers={
77
+ 'Authorization': 'bearer ' + token,
78
+ },
79
+ )
80
+
81
+ if response.status_code != 200:
82
+ raise StorageException(
83
+ 'Error reading cdstar object: ' + response.text + ' - ' + str(response.status_code)
84
+ )
85
+ return response
86
+
87
+ def update(self, token: str, storage_id: str, content: IO[bytes] | str, content_type: str):
88
+ """Update an object in dariahstorage.
89
+
90
+ Arguments:
91
+ token (str): authentication token
92
+ storage_id (str): the storage id
93
+ content (IO[bytes] | str): the data to store
94
+ content_type (str): content type
95
+
96
+ Raises:
97
+ Exception: if response from storage had no 201 status code
98
+ """
99
+
100
+ response = self._requests.put(
101
+ self._storage_url + '/' + storage_id,
102
+ headers={'Authorization': 'bearer ' + token, 'Content-type': content_type},
103
+ data=content,
104
+ )
105
+
106
+ if response.status_code != 201:
107
+ raise StorageException(
108
+ 'Error updating cdstar object '
109
+ + storage_id
110
+ + ': '
111
+ + response.text
112
+ + ' - '
113
+ + str(response.status_code)
114
+ )
115
+
116
+ def delete(self, token: str, storage_id: str):
117
+ """Delete an object from dariahstorage.
118
+
119
+ Args:
120
+ token (str): authentication token
121
+ storage_id (str): the storage id
122
+
123
+ Raises:
124
+ Exception: if response from storage had no 204 status code
125
+ """
126
+ response = self._requests.delete(
127
+ self._storage_url + '/' + storage_id,
128
+ headers={
129
+ 'Authorization': 'bearer ' + token,
130
+ },
131
+ )
132
+
133
+ if response.status_code != 204:
134
+ raise StorageException(
135
+ 'Error deleting cdstar object: ' + response.text + ' - ' + str(response.status_code)
136
+ )
137
+
138
+ def get_storage_url(self):
139
+ """Get the storage url
140
+
141
+ Returns:
142
+ str: the actual storage url set
143
+ """
144
+
145
+ return self._storage_url
146
+
147
+
148
+ class StorageException(Exception):
149
+ """Thrown in case of problems with the storage"""