rucio-clients 37.0.0rc1__py3-none-any.whl
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.
Potentially problematic release.
This version of rucio-clients might be problematic. Click here for more details.
- rucio/__init__.py +17 -0
- rucio/alembicrevision.py +15 -0
- rucio/cli/__init__.py +14 -0
- rucio/cli/account.py +216 -0
- rucio/cli/bin_legacy/__init__.py +13 -0
- rucio/cli/bin_legacy/rucio.py +2825 -0
- rucio/cli/bin_legacy/rucio_admin.py +2500 -0
- rucio/cli/command.py +272 -0
- rucio/cli/config.py +72 -0
- rucio/cli/did.py +191 -0
- rucio/cli/download.py +128 -0
- rucio/cli/lifetime_exception.py +33 -0
- rucio/cli/replica.py +162 -0
- rucio/cli/rse.py +293 -0
- rucio/cli/rule.py +158 -0
- rucio/cli/scope.py +40 -0
- rucio/cli/subscription.py +73 -0
- rucio/cli/upload.py +60 -0
- rucio/cli/utils.py +226 -0
- rucio/client/__init__.py +15 -0
- rucio/client/accountclient.py +432 -0
- rucio/client/accountlimitclient.py +183 -0
- rucio/client/baseclient.py +983 -0
- rucio/client/client.py +120 -0
- rucio/client/configclient.py +126 -0
- rucio/client/credentialclient.py +59 -0
- rucio/client/didclient.py +868 -0
- rucio/client/diracclient.py +56 -0
- rucio/client/downloadclient.py +1783 -0
- rucio/client/exportclient.py +44 -0
- rucio/client/fileclient.py +50 -0
- rucio/client/importclient.py +42 -0
- rucio/client/lifetimeclient.py +90 -0
- rucio/client/lockclient.py +109 -0
- rucio/client/metaconventionsclient.py +140 -0
- rucio/client/pingclient.py +44 -0
- rucio/client/replicaclient.py +452 -0
- rucio/client/requestclient.py +125 -0
- rucio/client/richclient.py +317 -0
- rucio/client/rseclient.py +746 -0
- rucio/client/ruleclient.py +294 -0
- rucio/client/scopeclient.py +90 -0
- rucio/client/subscriptionclient.py +173 -0
- rucio/client/touchclient.py +82 -0
- rucio/client/uploadclient.py +969 -0
- rucio/common/__init__.py +13 -0
- rucio/common/bittorrent.py +234 -0
- rucio/common/cache.py +111 -0
- rucio/common/checksum.py +168 -0
- rucio/common/client.py +122 -0
- rucio/common/config.py +788 -0
- rucio/common/constants.py +217 -0
- rucio/common/constraints.py +17 -0
- rucio/common/didtype.py +237 -0
- rucio/common/exception.py +1208 -0
- rucio/common/extra.py +31 -0
- rucio/common/logging.py +420 -0
- rucio/common/pcache.py +1409 -0
- rucio/common/plugins.py +185 -0
- rucio/common/policy.py +93 -0
- rucio/common/schema/__init__.py +200 -0
- rucio/common/schema/generic.py +416 -0
- rucio/common/schema/generic_multi_vo.py +395 -0
- rucio/common/stomp_utils.py +423 -0
- rucio/common/stopwatch.py +55 -0
- rucio/common/test_rucio_server.py +154 -0
- rucio/common/types.py +483 -0
- rucio/common/utils.py +1688 -0
- rucio/rse/__init__.py +96 -0
- rucio/rse/protocols/__init__.py +13 -0
- rucio/rse/protocols/bittorrent.py +194 -0
- rucio/rse/protocols/cache.py +111 -0
- rucio/rse/protocols/dummy.py +100 -0
- rucio/rse/protocols/gfal.py +708 -0
- rucio/rse/protocols/globus.py +243 -0
- rucio/rse/protocols/http_cache.py +82 -0
- rucio/rse/protocols/mock.py +123 -0
- rucio/rse/protocols/ngarc.py +209 -0
- rucio/rse/protocols/posix.py +250 -0
- rucio/rse/protocols/protocol.py +361 -0
- rucio/rse/protocols/rclone.py +365 -0
- rucio/rse/protocols/rfio.py +145 -0
- rucio/rse/protocols/srm.py +338 -0
- rucio/rse/protocols/ssh.py +414 -0
- rucio/rse/protocols/storm.py +195 -0
- rucio/rse/protocols/webdav.py +594 -0
- rucio/rse/protocols/xrootd.py +302 -0
- rucio/rse/rsemanager.py +881 -0
- rucio/rse/translation.py +260 -0
- rucio/vcsversion.py +11 -0
- rucio/version.py +45 -0
- rucio_clients-37.0.0rc1.data/data/etc/rse-accounts.cfg.template +25 -0
- rucio_clients-37.0.0rc1.data/data/etc/rucio.cfg.atlas.client.template +43 -0
- rucio_clients-37.0.0rc1.data/data/etc/rucio.cfg.template +241 -0
- rucio_clients-37.0.0rc1.data/data/requirements.client.txt +19 -0
- rucio_clients-37.0.0rc1.data/data/rucio_client/merge_rucio_configs.py +144 -0
- rucio_clients-37.0.0rc1.data/scripts/rucio +133 -0
- rucio_clients-37.0.0rc1.data/scripts/rucio-admin +97 -0
- rucio_clients-37.0.0rc1.dist-info/METADATA +54 -0
- rucio_clients-37.0.0rc1.dist-info/RECORD +104 -0
- rucio_clients-37.0.0rc1.dist-info/WHEEL +5 -0
- rucio_clients-37.0.0rc1.dist-info/licenses/AUTHORS.rst +100 -0
- rucio_clients-37.0.0rc1.dist-info/licenses/LICENSE +201 -0
- rucio_clients-37.0.0rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import enum
|
|
16
|
+
from collections import namedtuple
|
|
17
|
+
from typing import Literal, get_args
|
|
18
|
+
|
|
19
|
+
from rucio.common.config import config_get_bool
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
Constants.
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
RESERVED_KEYS = ['scope', 'name', 'account', 'did_type', 'is_open', 'monotonic', 'obsolete', 'complete',
|
|
27
|
+
'availability', 'suppressed', 'bytes', 'length', 'md5', 'adler32', 'rule_evaluation_action',
|
|
28
|
+
'rule_evaluation_required', 'expired_at', 'deleted_at', 'created_at', 'updated_at']
|
|
29
|
+
# collection_keys =
|
|
30
|
+
# file_keys =
|
|
31
|
+
|
|
32
|
+
KEY_TYPES = ['ALL', 'COLLECTION', 'FILE', 'DERIVED']
|
|
33
|
+
# all(container, dataset, file), collection(dataset or container), file, derived(compute from file for collection)
|
|
34
|
+
|
|
35
|
+
SCHEME_MAP = {'srm': ['srm', 'gsiftp'],
|
|
36
|
+
'gsiftp': ['srm', 'gsiftp'],
|
|
37
|
+
'https': ['https', 'davs', 'srm+https', 'cs3s'],
|
|
38
|
+
'davs': ['https', 'davs', 'srm+https', 'cs3s'],
|
|
39
|
+
'srm+https': ['https', 'davs', 'srm+https', 'cs3s'],
|
|
40
|
+
'cs3s': ['https', 'davs', 'srm+https', 'cs3s'],
|
|
41
|
+
'root': ['root'],
|
|
42
|
+
'scp': ['scp'],
|
|
43
|
+
'rsync': ['rsync'],
|
|
44
|
+
'rclone': ['rclone']}
|
|
45
|
+
if config_get_bool('transfers', 'srm_https_compatibility', raise_exception=False, default=False):
|
|
46
|
+
SCHEME_MAP['srm'].append('https')
|
|
47
|
+
SCHEME_MAP['https'].append('srm')
|
|
48
|
+
SCHEME_MAP['srm'].append('davs')
|
|
49
|
+
SCHEME_MAP['davs'].append('srm')
|
|
50
|
+
|
|
51
|
+
SORTING_ALGORITHMS_LITERAL = Literal['geoip', 'custom_table', 'random']
|
|
52
|
+
SORTING_ALGORITHMS = list(get_args(SORTING_ALGORITHMS_LITERAL))
|
|
53
|
+
|
|
54
|
+
SUPPORTED_PROTOCOLS_LITERAL = Literal['gsiftp', 'srm', 'root', 'davs', 'http', 'https', 'file', 'storm', 'srm+https', 'scp', 'rsync', 'rclone', 'magnet']
|
|
55
|
+
SUPPORTED_PROTOCOLS: list[str] = list(get_args(SUPPORTED_PROTOCOLS_LITERAL))
|
|
56
|
+
|
|
57
|
+
RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL = Literal['ALL', 'LAN', 'WAN']
|
|
58
|
+
|
|
59
|
+
RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL = Literal['read', 'write', 'delete']
|
|
60
|
+
RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS: list[str] = list(get_args(RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL))
|
|
61
|
+
|
|
62
|
+
RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL = Literal[RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL, 'third_party_copy_read', 'third_party_copy_write']
|
|
63
|
+
RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS: list[str] = list(get_args(RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL))
|
|
64
|
+
|
|
65
|
+
FTS_STATE = namedtuple('FTS_STATE', ['SUBMITTED', 'READY', 'ACTIVE', 'FAILED', 'FINISHED', 'FINISHEDDIRTY', 'NOT_USED',
|
|
66
|
+
'CANCELED'])('SUBMITTED', 'READY', 'ACTIVE', 'FAILED', 'FINISHED', 'FINISHEDDIRTY',
|
|
67
|
+
'NOT_USED', 'CANCELED')
|
|
68
|
+
|
|
69
|
+
FTS_COMPLETE_STATE = namedtuple('FTS_COMPLETE_STATE', ['OK', 'ERROR'])('Ok', 'Error')
|
|
70
|
+
|
|
71
|
+
# https://gitlab.cern.ch/fts/fts3/-/blob/master/src/db/generic/Job.h#L41
|
|
72
|
+
FTS_JOB_TYPE = namedtuple('FTS_JOB_TYPE', ['MULTIPLE_REPLICA', 'MULTI_HOP', 'SESSION_REUSE', 'REGULAR'])('R', 'H', 'Y', 'N')
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Messages constants
|
|
76
|
+
|
|
77
|
+
MAX_MESSAGE_LENGTH = 4000
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@enum.unique
|
|
81
|
+
class SuspiciousAvailability(enum.Enum):
|
|
82
|
+
ALL = 0
|
|
83
|
+
EXIST_COPIES = 1
|
|
84
|
+
LAST_COPY = 2
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@enum.unique
|
|
88
|
+
class ReplicaState(enum.Enum):
|
|
89
|
+
# From rucio.db.sqla.constants, update that file at the same time as this
|
|
90
|
+
AVAILABLE = 'A'
|
|
91
|
+
UNAVAILABLE = 'U'
|
|
92
|
+
COPYING = 'C'
|
|
93
|
+
BEING_DELETED = 'B'
|
|
94
|
+
BAD = 'D'
|
|
95
|
+
TEMPORARY_UNAVAILABLE = 'T'
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@enum.unique
|
|
99
|
+
class HermesService(str, enum.Enum):
|
|
100
|
+
"""
|
|
101
|
+
The services supported by Hermes2.
|
|
102
|
+
"""
|
|
103
|
+
INFLUX = "INFLUX"
|
|
104
|
+
ELASTIC = "ELASTIC"
|
|
105
|
+
EMAIL = "EMAIL"
|
|
106
|
+
ACTIVEMQ = "ACTIVEMQ"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class RseAttr:
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
List of functional RSE attributes.
|
|
113
|
+
|
|
114
|
+
This class acts as a namespace containing all RSE attributes referenced in
|
|
115
|
+
the Rucio source code. Setting them affects Rucio's behaviour in some way.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
ARCHIVE_TIMEOUT = 'archive_timeout'
|
|
119
|
+
ASSOCIATED_SITES = 'associated_sites'
|
|
120
|
+
AUTO_APPROVE_BYTES = 'auto_approve_bytes'
|
|
121
|
+
AUTO_APPROVE_FILES = 'auto_approve_files'
|
|
122
|
+
BITTORRENT_TRACKER_ADDR = 'bittorrent_tracker_addr'
|
|
123
|
+
BLOCK_MANUAL_APPROVAL = 'block_manual_approval'
|
|
124
|
+
COUNTRY = 'country'
|
|
125
|
+
DECOMMISSION = 'decommission'
|
|
126
|
+
DEFAULT_ACCOUNT_LIMIT_BYTES = 'default_account_limit_bytes'
|
|
127
|
+
FTS = 'fts'
|
|
128
|
+
GLOBUS_ENDPOINT_ID = 'globus_endpoint_id'
|
|
129
|
+
GREEDYDELETION = 'greedyDeletion'
|
|
130
|
+
IS_OBJECT_STORE = 'is_object_store'
|
|
131
|
+
LFN2PFN_ALGORITHM = 'lfn2pfn_algorithm'
|
|
132
|
+
MAXIMUM_PIN_LIFETIME = 'maximum_pin_lifetime'
|
|
133
|
+
MULTIHOP_TOMBSTONE_DELAY = 'multihop_tombstone_delay'
|
|
134
|
+
NAMING_CONVENTION = 'naming_convention'
|
|
135
|
+
OIDC_BASE_PATH = 'oidc_base_path'
|
|
136
|
+
OIDC_SUPPORT = 'oidc_support'
|
|
137
|
+
PHYSGROUP = 'physgroup'
|
|
138
|
+
QBITTORRENT_MANAGEMENT_ADDRESS = 'qbittorrent_management_address'
|
|
139
|
+
RESTRICTED_READ = 'restricted_read'
|
|
140
|
+
RESTRICTED_WRITE = 'restricted_write'
|
|
141
|
+
RULE_APPROVERS = 'rule_approvers'
|
|
142
|
+
S3_URL_STYLE = 's3_url_style'
|
|
143
|
+
SIGN_URL = 'sign_url'
|
|
144
|
+
SIMULATE_MULTIRANGE = 'simulate_multirange'
|
|
145
|
+
SITE = 'site'
|
|
146
|
+
SKIP_UPLOAD_STAT = 'skip_upload_stat'
|
|
147
|
+
SOURCE_FOR_TOTAL_SPACE = 'source_for_total_space'
|
|
148
|
+
SOURCE_FOR_USED_SPACE = 'source_for_used_space'
|
|
149
|
+
STAGING_BUFFER = 'staging_buffer'
|
|
150
|
+
STAGING_REQUIRED = 'staging_required'
|
|
151
|
+
STRICT_COPY = 'strict_copy'
|
|
152
|
+
TOMBSTONE_DELAY = 'tombstone_delay'
|
|
153
|
+
TYPE = 'type'
|
|
154
|
+
USE_IPV4 = 'use_ipv4'
|
|
155
|
+
VERIFY_CHECKSUM = 'verify_checksum'
|
|
156
|
+
|
|
157
|
+
# The following RSE attributes are exclusively used in the permission layer
|
|
158
|
+
# and are likely VO-specific.
|
|
159
|
+
|
|
160
|
+
BLOCK_MANUAL_APPROVE = 'block_manual_approve'
|
|
161
|
+
CMS_TYPE = 'cms_type'
|
|
162
|
+
DEFAULT_LIMIT_FILES = 'default_limit_files'
|
|
163
|
+
QUOTA_APPROVERS = 'quota_approvers'
|
|
164
|
+
RULE_DELETERS = 'rule_deleters'
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Literal types to allow overloading of functions with RSE attributes in their signature.
|
|
168
|
+
# RSE attributes are encoded via the BooleanString decorator as VARCHAR in the database,
|
|
169
|
+
# but they are used as either bool or string in the code.
|
|
170
|
+
# This is only determined at runtime, so for static type checking
|
|
171
|
+
# we need to manually specify which attrs are string and which are bool.
|
|
172
|
+
# In future, we could refactor RseAttr to avoid code duplication.
|
|
173
|
+
RSE_ATTRS_STR = Literal[
|
|
174
|
+
'archive_timeout',
|
|
175
|
+
'associated_sites',
|
|
176
|
+
'bittorrent_tracker_addr',
|
|
177
|
+
'country',
|
|
178
|
+
'decommission',
|
|
179
|
+
'default_account_limit_bytes',
|
|
180
|
+
'fts',
|
|
181
|
+
'globus_endpoint_id',
|
|
182
|
+
'lfn2pfn_algorithm',
|
|
183
|
+
'maximum_pin_lifetime',
|
|
184
|
+
'multihop_tombstone_delay',
|
|
185
|
+
'naming_convention',
|
|
186
|
+
'oidc_base_path',
|
|
187
|
+
'oidc_support'
|
|
188
|
+
'physgroup',
|
|
189
|
+
'qbittorrent_management_address'
|
|
190
|
+
'rule_approvers',
|
|
191
|
+
's3_url_style',
|
|
192
|
+
'simulate_multirange',
|
|
193
|
+
'site',
|
|
194
|
+
'source_for_total_space',
|
|
195
|
+
'source_for_used_space',
|
|
196
|
+
'staging_buffer',
|
|
197
|
+
'tombstone_delay',
|
|
198
|
+
'type'
|
|
199
|
+
]
|
|
200
|
+
|
|
201
|
+
RSE_ATTRS_BOOL = Literal[
|
|
202
|
+
'auto_approve_bytes',
|
|
203
|
+
'auto_approve_files',
|
|
204
|
+
'block_manual_approval',
|
|
205
|
+
'greedyDeletion',
|
|
206
|
+
'is_object_store',
|
|
207
|
+
'restricted_read',
|
|
208
|
+
'restricted_write',
|
|
209
|
+
'skip_upload_stat',
|
|
210
|
+
'staging_required',
|
|
211
|
+
'strict_copy',
|
|
212
|
+
'use_ipv4',
|
|
213
|
+
'verify_checksum'
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
SUPPORTED_SIGN_URL_SERVICES_LITERAL = Literal['gcs', 's3', 'swift']
|
|
217
|
+
SUPPORTED_SIGN_URL_SERVICES = list(get_args(SUPPORTED_SIGN_URL_SERVICES_LITERAL))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# List of authorized value types for key
|
|
16
|
+
AUTHORIZED_VALUE_TYPES = (float, int, str)
|
|
17
|
+
STRING_TYPES = (str, )
|
rucio/common/didtype.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
DID type to represent a did and to simplify operations on it
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from typing import Any, Union
|
|
20
|
+
|
|
21
|
+
from rucio.common.exception import DIDError
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DID:
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
Class used to store a DID
|
|
28
|
+
Given an object did of type DID
|
|
29
|
+
scope is stored in did.scope
|
|
30
|
+
name is stored in did.name
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
SCOPE_SEPARATOR = ':'
|
|
34
|
+
IMPLICIT_SCOPE_SEPARATOR = '.'
|
|
35
|
+
IMPLICIT_SCOPE_TO_LEN = {'user': 2, 'group': 2}
|
|
36
|
+
__slots__ = ['scope', 'name']
|
|
37
|
+
|
|
38
|
+
def __init__(self, *args, **kwargs):
|
|
39
|
+
"""
|
|
40
|
+
Constructs the DID object. Possible parameter combinations are:
|
|
41
|
+
DID()
|
|
42
|
+
DID('scope:name.did.str')
|
|
43
|
+
DID('user.implicit.scope.in.name')
|
|
44
|
+
DID('custom.scope', 'custom.name')
|
|
45
|
+
DID(['list.scope', 'list.name'])
|
|
46
|
+
DID(('tuple.scope', 'tuple.name'))
|
|
47
|
+
DID({'scope': 'dict.scope', 'name': 'dict.name'})
|
|
48
|
+
DID(scope='kw.scope')
|
|
49
|
+
DID(name='kw.name')
|
|
50
|
+
DID(name='user.kw.implicit.scope')
|
|
51
|
+
DID(scope='kw.scope', name='kw.name')
|
|
52
|
+
DID(did={'scope': 'kw.did.scope', 'name': 'kw.did.name'})
|
|
53
|
+
DID(did=['kw.list.scope', 'kw.list.name'])
|
|
54
|
+
DID(did=('kw.tuple.scope', 'kw.tuple.name'))
|
|
55
|
+
DID('arg.scope', name='kwarg.name')
|
|
56
|
+
DID('arg.name', scope='kwarg.scope')
|
|
57
|
+
"""
|
|
58
|
+
self.scope: str = ''
|
|
59
|
+
self.name: str = ''
|
|
60
|
+
|
|
61
|
+
did = self._parse_did_from_args(*args, **kwargs)
|
|
62
|
+
|
|
63
|
+
self._construct_did(did)
|
|
64
|
+
|
|
65
|
+
if not self.is_valid_format():
|
|
66
|
+
raise DIDError('Object has invalid format after construction: {}'.format(str(self)))
|
|
67
|
+
|
|
68
|
+
def _parse_did_from_args(
|
|
69
|
+
self,
|
|
70
|
+
*args,
|
|
71
|
+
**kwargs
|
|
72
|
+
) -> Union["DID", str, tuple[str, str], list[str], dict[str, str]]:
|
|
73
|
+
"""
|
|
74
|
+
Parse the DID object from the given arguments
|
|
75
|
+
:return: DID object
|
|
76
|
+
"""
|
|
77
|
+
num_args = len(args)
|
|
78
|
+
num_kwargs = len(kwargs)
|
|
79
|
+
if (num_args + num_kwargs) > 2:
|
|
80
|
+
raise DIDError('Constructor takes at most 2 arguments. Given number: {}'.format(num_args + num_kwargs))
|
|
81
|
+
|
|
82
|
+
did: Union["DID", str, tuple[str, str], list[str], dict[str, str]] = ''
|
|
83
|
+
if num_args == 1:
|
|
84
|
+
did = args[0]
|
|
85
|
+
|
|
86
|
+
if num_kwargs == 1:
|
|
87
|
+
if isinstance(did, str):
|
|
88
|
+
k, v = next(iter(kwargs.items()))
|
|
89
|
+
if k == 'scope':
|
|
90
|
+
did = (v, did)
|
|
91
|
+
elif k == 'name':
|
|
92
|
+
did = (did, v)
|
|
93
|
+
else:
|
|
94
|
+
raise DIDError('Constructor got unexpected keyword argument: {}'.format(k))
|
|
95
|
+
else:
|
|
96
|
+
raise DIDError('First argument of constructor is expected to be string type '
|
|
97
|
+
'when keyword argument is given. Given type: {}'.format(type(did)))
|
|
98
|
+
elif num_args == 0:
|
|
99
|
+
did = kwargs.get('did', kwargs)
|
|
100
|
+
else:
|
|
101
|
+
did = args
|
|
102
|
+
return did
|
|
103
|
+
|
|
104
|
+
def _construct_did(self, did: Any) -> None:
|
|
105
|
+
"""
|
|
106
|
+
Construct the DID object from the given input.
|
|
107
|
+
|
|
108
|
+
:param did: input to construct the DID object from
|
|
109
|
+
"""
|
|
110
|
+
if isinstance(did, dict):
|
|
111
|
+
self._did_from_dict(did)
|
|
112
|
+
elif isinstance(did, tuple) or isinstance(did, list):
|
|
113
|
+
self._did_from_list_or_tuple(did)
|
|
114
|
+
elif isinstance(did, str):
|
|
115
|
+
self._did_from_str(did)
|
|
116
|
+
elif isinstance(did, DID):
|
|
117
|
+
self._did_from_did_object(did)
|
|
118
|
+
else:
|
|
119
|
+
raise DIDError('Cannot build object from: {}'.format(type(did)))
|
|
120
|
+
|
|
121
|
+
if self.name.endswith('/'):
|
|
122
|
+
self.name = self.name[:-1]
|
|
123
|
+
|
|
124
|
+
def _did_from_str(self, did: str) -> None:
|
|
125
|
+
"""
|
|
126
|
+
Construct the DID from a string.
|
|
127
|
+
:param did: string containing the DID information
|
|
128
|
+
"""
|
|
129
|
+
did_parts = did.split(DID.SCOPE_SEPARATOR, 1)
|
|
130
|
+
if len(did_parts) == 1:
|
|
131
|
+
self.name = did
|
|
132
|
+
self._update_implicit_scope()
|
|
133
|
+
if not self.has_scope():
|
|
134
|
+
raise DIDError('Object construction from non-splitable string is ambigious')
|
|
135
|
+
else:
|
|
136
|
+
self.scope = did_parts[0]
|
|
137
|
+
self.name = did_parts[1]
|
|
138
|
+
|
|
139
|
+
def _did_from_dict(self, did: dict[str, str]) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Construct the DID from a dictionary.
|
|
142
|
+
:param did: dictionary optionally containing the keys 'scope' and 'name'
|
|
143
|
+
"""
|
|
144
|
+
self.scope = did.get('scope', '')
|
|
145
|
+
self.name = did.get('name', '')
|
|
146
|
+
if not self.has_scope():
|
|
147
|
+
self._update_implicit_scope()
|
|
148
|
+
|
|
149
|
+
def _did_from_list_or_tuple(self, did: Union[list[str], tuple[str, str]]) -> None:
|
|
150
|
+
"""
|
|
151
|
+
Construct the DID from a list or tuple.
|
|
152
|
+
:param did: list or tuple with expected length of 2
|
|
153
|
+
"""
|
|
154
|
+
if len(did) != 2:
|
|
155
|
+
raise DIDError('Construction from tuple or list requires exactly 2 elements. Number of elements passed: %i' % len(did))
|
|
156
|
+
self.scope = did[0]
|
|
157
|
+
self.name = did[1]
|
|
158
|
+
|
|
159
|
+
def _did_from_did_object(self, did: "DID") -> None:
|
|
160
|
+
"""
|
|
161
|
+
Construct the DID from another DID object.
|
|
162
|
+
:param did: DID object
|
|
163
|
+
"""
|
|
164
|
+
self.scope = did.scope
|
|
165
|
+
self.name = did.name
|
|
166
|
+
|
|
167
|
+
def _update_implicit_scope(self) -> None:
|
|
168
|
+
"""
|
|
169
|
+
This method sets the scope if it is implicitly given in self.name
|
|
170
|
+
"""
|
|
171
|
+
did_parts = self.name.split(DID.IMPLICIT_SCOPE_SEPARATOR)
|
|
172
|
+
num_scope_parts = DID.IMPLICIT_SCOPE_TO_LEN.get(did_parts[0], 0)
|
|
173
|
+
if num_scope_parts > 0:
|
|
174
|
+
self.scope = '.'.join(did_parts[0:num_scope_parts])
|
|
175
|
+
|
|
176
|
+
def is_valid_format(self) -> bool:
|
|
177
|
+
"""
|
|
178
|
+
Method to check if the stored DID has a valid format
|
|
179
|
+
:return: bool
|
|
180
|
+
"""
|
|
181
|
+
if self.scope.count(DID.SCOPE_SEPARATOR) or self.name.count(DID.SCOPE_SEPARATOR):
|
|
182
|
+
return False
|
|
183
|
+
return True
|
|
184
|
+
|
|
185
|
+
def has_scope(self) -> bool:
|
|
186
|
+
"""
|
|
187
|
+
Method to check if the scope part was set
|
|
188
|
+
:return: bool
|
|
189
|
+
"""
|
|
190
|
+
return len(self.scope) > 0
|
|
191
|
+
|
|
192
|
+
def has_name(self) -> bool:
|
|
193
|
+
"""
|
|
194
|
+
Method to check if the name part was set
|
|
195
|
+
:return: bool
|
|
196
|
+
"""
|
|
197
|
+
return len(self.name) > 0
|
|
198
|
+
|
|
199
|
+
def __str__(self) -> str:
|
|
200
|
+
"""
|
|
201
|
+
Creates the string representation of self
|
|
202
|
+
:return: string
|
|
203
|
+
"""
|
|
204
|
+
if self.has_scope() and self.has_name():
|
|
205
|
+
return '{}{}{}'.format(self.scope, DID.SCOPE_SEPARATOR, self.name)
|
|
206
|
+
elif self.has_scope():
|
|
207
|
+
return self.scope
|
|
208
|
+
return self.name
|
|
209
|
+
|
|
210
|
+
def __eq__(self, other: Union[str, "DID"]) -> bool:
|
|
211
|
+
"""
|
|
212
|
+
Equality comparison with another object
|
|
213
|
+
:return: bool
|
|
214
|
+
"""
|
|
215
|
+
if isinstance(other, str):
|
|
216
|
+
return str(self) == other
|
|
217
|
+
elif not isinstance(other, DID):
|
|
218
|
+
try:
|
|
219
|
+
other = DID(other)
|
|
220
|
+
except DIDError:
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
return self.scope == other.scope and self.name == other.name
|
|
224
|
+
|
|
225
|
+
def __ne__(self, other: Union[str, "DID"]) -> bool:
|
|
226
|
+
"""
|
|
227
|
+
Inequality comparison with another object
|
|
228
|
+
:return: bool
|
|
229
|
+
"""
|
|
230
|
+
return not self.__eq__(other)
|
|
231
|
+
|
|
232
|
+
def __hash__(self) -> int:
|
|
233
|
+
"""
|
|
234
|
+
Uses the string representation of self to create a hash
|
|
235
|
+
:return: int
|
|
236
|
+
"""
|
|
237
|
+
return hash(str(self))
|