edrive-anyshare 1.0.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,2 @@
1
+ include README.md
2
+ recursive-include tests *.py
@@ -0,0 +1,395 @@
1
+ Metadata-Version: 2.4
2
+ Name: edrive-anyshare
3
+ Version: 1.0.0
4
+ Summary: AnyShare eDrive login, directory, upload, and sharing client
5
+ Project-URL: Documentation, https://developers.aishutech.com/napi/documents/307
6
+ Keywords: anyshare,edrive,file-sharing,enterprise-content-management
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Topic :: Internet
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+
16
+ # AnyShare eDrive Python package
17
+
18
+ This is a tenant-neutral Python client for AnyShare and eDrive deployments.
19
+ The server URL, account, password, and optional browser-login public key are
20
+ provided by the application, so the package is not tied to one provider or
21
+ domain.
22
+
23
+ It supports:
24
+
25
+ - browser-style AnyShare login
26
+ - reusable cookie-based sessions
27
+ - owned document-library and directory operations
28
+ - remote path resolution and directory creation
29
+ - recursive folder uploads
30
+ - permanent anonymous share-link reuse or creation
31
+ - authenticated requests to other supported AnyShare endpoints
32
+
33
+ The package uses the browser-login and legacy document/share endpoint shapes
34
+ implemented by the reference client. AnyShare deployments can expose
35
+ different features or endpoint versions; confirm the API contract for the
36
+ target tenant before enabling write operations.
37
+
38
+ ## Requirements
39
+
40
+ - Python 3.9 or newer
41
+ - curl
42
+ - openssl
43
+
44
+ There are no third-party runtime dependencies. HTTPS connections use the
45
+ system curl installation and its certificate store.
46
+
47
+ ## Install
48
+
49
+ Install the published distribution:
50
+
51
+ ```bash
52
+ python -m pip install edrive-anyshare
53
+ ```
54
+
55
+ Install this source tree for development:
56
+
57
+ ```bash
58
+ cd /path/to/edrive
59
+ python -m pip install -e .
60
+ ```
61
+
62
+ The import name is edrive and the distribution name is edrive-anyshare.
63
+
64
+ ## Configuration
65
+
66
+ The library accepts credentials explicitly and does not require or load an
67
+ environment file. This avoids silently selecting credentials or a tenant in a
68
+ library process.
69
+
70
+ For a shell-based application:
71
+
72
+ ```bash
73
+ export EDRIVE_BASE_URL=https://anyshare.example.com
74
+ export EDRIVE_USERNAME=your-account
75
+ export EDRIVE_PASSWORD=your-password
76
+ ```
77
+
78
+ An application may read these optional variables with its own configuration
79
+ system and pass them to login:
80
+
81
+ ```python
82
+ import os
83
+
84
+ from edrive import login
85
+
86
+ with login(
87
+ os.environ["EDRIVE_USERNAME"],
88
+ os.environ["EDRIVE_PASSWORD"],
89
+ os.environ["EDRIVE_BASE_URL"],
90
+ ) as session:
91
+ print(session.username)
92
+ ```
93
+
94
+ The base URL must be an absolute http or https URL. Trailing slashes are
95
+ removed automatically, and query strings or fragments are rejected.
96
+
97
+ ## Quick start: upload a folder
98
+
99
+ ```python
100
+ from edrive import ONDUP_OVERWRITE, login, upload_folder
101
+
102
+ with login(
103
+ "your-account",
104
+ "your-password",
105
+ "https://anyshare.example.com",
106
+ ) as session:
107
+ result = upload_folder(
108
+ session,
109
+ "/path/to/local/folder",
110
+ "Documents/Reports",
111
+ ondup=ONDUP_OVERWRITE,
112
+ create_share_link=True,
113
+ )
114
+ print(result.share_url)
115
+ ```
116
+
117
+ The first component of a remote path is an owned document-library name.
118
+ Missing nested folders are created by default. Pass remote_docid instead of
119
+ remote_path to skip library and path lookup:
120
+
121
+ ```python
122
+ result = upload_folder(
123
+ session,
124
+ "/path/to/local/folder",
125
+ remote_docid="gns://library/folder-id",
126
+ create_share_link=False,
127
+ )
128
+ ```
129
+
130
+ If both remote_path and remote_docid are supplied, remote_docid takes
131
+ precedence. The default duplicate policy is ONDUP_OVERWRITE; use
132
+ ONDUP_RENAME to keep both files.
133
+
134
+ ## Complete demonstration
135
+
136
+ The following script does not use an environment file. It asks for the tenant
137
+ URL, account, and password, lists the account's document libraries, and asks
138
+ for confirmation before uploading a local folder:
139
+
140
+ ```python
141
+ from getpass import getpass
142
+ from pathlib import Path
143
+
144
+ from edrive import (
145
+ ONDUP_OVERWRITE,
146
+ list_owned_doc_libs,
147
+ login,
148
+ upload_folder,
149
+ )
150
+
151
+ base_url = input("AnyShare URL: ").strip()
152
+ username = input("Account: ").strip()
153
+ password = getpass("Password: ")
154
+ local_folder = Path(input("Local folder: ").strip()).expanduser()
155
+ remote_path = input("Remote path, for example Documents/Reports: ").strip()
156
+
157
+ with login(username, password, base_url) as session:
158
+ libraries = list_owned_doc_libs(session)
159
+ print("Owned document libraries:")
160
+ for library in libraries:
161
+ print(" -", library.get("name"), library.get("id"))
162
+
163
+ if input("Upload this folder? [y/N] ").strip().lower() == "y":
164
+ result = upload_folder(
165
+ session,
166
+ local_folder,
167
+ remote_path,
168
+ ondup=ONDUP_OVERWRITE,
169
+ create_share_link=True,
170
+ )
171
+ print("Uploaded files:", len(result.uploaded_files))
172
+ print("Share URL:", result.share_url or "(not created)")
173
+ ```
174
+
175
+ For a read-only connection check, stop after list_owned_doc_libs. Uploading
176
+ and share-link creation are write operations and depend on tenant permissions.
177
+
178
+ ## Authentication
179
+
180
+ ### login
181
+
182
+ ```python
183
+ login(
184
+ username,
185
+ password,
186
+ base_url,
187
+ *,
188
+ cookiejar=None,
189
+ login_public_key=None,
190
+ )
191
+ ```
192
+
193
+ Returns an EdriveSession. The session follows the AnyShare web login
194
+ redirects, submits the CSRF/challenge login payload, encrypts the password
195
+ with RSA through openssl, and reads the OAuth token from the cookie jar.
196
+
197
+ Use the session as a context manager. A temporary cookie jar is deleted when
198
+ the context closes. A caller-provided cookie jar is retained on disk, but the
199
+ session object clears its reference after close:
200
+
201
+ ```python
202
+ with login(
203
+ "your-account",
204
+ "your-password",
205
+ "https://anyshare.example.com",
206
+ cookiejar="/tmp/anyshare.cookies",
207
+ ) as session:
208
+ pass
209
+ ```
210
+
211
+ The package exports LOGIN_PUBLIC_KEY as the bundled default public key:
212
+
213
+ ```python
214
+ from edrive import LOGIN_PUBLIC_KEY
215
+ ```
216
+
217
+ If a tenant uses a different browser-login key, provide its PEM contents:
218
+
219
+ ```python
220
+ from pathlib import Path
221
+
222
+ from edrive import login
223
+
224
+ tenant_key = Path("/secure/path/tenant-login-public-key.pem").read_text()
225
+
226
+ with login(
227
+ "your-account",
228
+ "your-password",
229
+ "https://anyshare.example.com",
230
+ login_public_key=tenant_key,
231
+ ) as session:
232
+ pass
233
+ ```
234
+
235
+ The public key is not a password and does not need to be kept secret. Do not
236
+ commit private keys, passwords, cookie jars, or real credentials.
237
+
238
+ ## Browse document libraries and folders
239
+
240
+ ```python
241
+ from edrive import (
242
+ create_dir,
243
+ find_child_dir,
244
+ list_dir,
245
+ list_owned_doc_libs,
246
+ login,
247
+ resolve_docid_by_name,
248
+ resolve_folder_path,
249
+ )
250
+
251
+ with login("user", "password", "https://anyshare.example.com") as session:
252
+ libraries = list_owned_doc_libs(session)
253
+ library_id = resolve_docid_by_name(session, "Documents")
254
+
255
+ listing = list_dir(session, library_id)
256
+ print(listing.get("dirs", []))
257
+ print(listing.get("files", []))
258
+
259
+ reports = find_child_dir(session, library_id, "Reports")
260
+ if reports is None:
261
+ reports = create_dir(session, library_id, "Reports")
262
+
263
+ folder_id, parent_id, created = resolve_folder_path(
264
+ session,
265
+ "Documents/Reports/2026",
266
+ create=True,
267
+ )
268
+ print(folder_id, parent_id, created)
269
+ ```
270
+
271
+ Directory and library responses are returned as dictionaries containing the
272
+ fields supplied by the tenant. Document IDs may be ordinary IDs or AnyShare
273
+ URI-style values.
274
+
275
+ ## Upload results
276
+
277
+ upload_folder returns UploadResult:
278
+
279
+ ```python
280
+ result.share_url
281
+ result.share_id
282
+ result.share_link_created
283
+ result.local_path
284
+ result.remote_path
285
+ result.remote_folder_name
286
+ result.remote_folder_docid
287
+ result.remote_parent_docid
288
+ result.uploaded_files
289
+ result.created_dirs
290
+ ```
291
+
292
+ Use result.to_dict() when the result must be serialized as JSON:
293
+
294
+ ```python
295
+ payload = result.to_dict()
296
+ ```
297
+
298
+ The upload process uses the AnyShare begin-upload, direct multipart upload,
299
+ and end-upload sequence. If an upload fails after a remote folder has been
300
+ created, the client does not delete that remote content automatically.
301
+
302
+ ## Share links
303
+
304
+ ```python
305
+ from edrive import (
306
+ create_anonymous_share_link,
307
+ get_or_create_permanent_share_link,
308
+ list_share_links,
309
+ )
310
+
311
+ links = list_share_links(session, "gns://library/folder-id")
312
+
313
+ link_id = create_anonymous_share_link(
314
+ session,
315
+ "gns://library/folder-id",
316
+ title="Reports",
317
+ allow=["display", "preview", "download"],
318
+ )
319
+
320
+ share = get_or_create_permanent_share_link(
321
+ session,
322
+ "gns://library/folder-id",
323
+ title="Reports",
324
+ )
325
+ print(share["url"], share["created"])
326
+ ```
327
+
328
+ The helper reuses a permanent link when one already exists. A tenant may
329
+ return HTTP 202 when link creation requires approval; this is reported as a
330
+ typed error.
331
+
332
+ ## Authenticated API requests
333
+
334
+ Use api_request for an endpoint not wrapped by a convenience function:
335
+
336
+ ```python
337
+ from edrive import api_request
338
+
339
+ status, data = api_request(
340
+ session,
341
+ "GET",
342
+ "/api/efast/v1/owned-doc-lib",
343
+ )
344
+ if status == 200:
345
+ print(data)
346
+ ```
347
+
348
+ The function:
349
+
350
+ - accepts relative paths or absolute http(s) URLs
351
+ - adds the session Bearer token when present
352
+ - encodes json_body as JSON
353
+ - returns (status, decoded_data)
354
+ - returns text when the response is not JSON
355
+ - returns None for an empty response
356
+
357
+ The exported default endpoint prefixes are API_PREFIX and
358
+ SHARE_LINK_PREFIX. They are kept in one module so tenant-specific endpoint
359
+ adaptations can be made without changing the public operation signatures.
360
+
361
+ ## Errors
362
+
363
+ All client-specific errors inherit from EdriveError, which inherits from
364
+ RuntimeError:
365
+
366
+ ```python
367
+ from edrive import (
368
+ EdriveAuthenticationError,
369
+ EdriveError,
370
+ EdriveHTTPError,
371
+ EdriveProtocolError,
372
+ EdriveTransportError,
373
+ EdriveUploadError,
374
+ )
375
+
376
+ try:
377
+ with login("user", "password", "https://anyshare.example.com") as session:
378
+ pass
379
+ except EdriveAuthenticationError:
380
+ print("The tenant rejected the login.")
381
+ except EdriveTransportError:
382
+ print("curl or the network was unavailable.")
383
+ except EdriveHTTPError:
384
+ print("AnyShare returned an unsuccessful status.")
385
+ except EdriveProtocolError:
386
+ print("The tenant response did not match the expected shape.")
387
+ except EdriveUploadError:
388
+ print("The direct upload failed.")
389
+ except EdriveError:
390
+ print("Another eDrive client error occurred.")
391
+ ```
392
+
393
+ Passwords, access tokens, authorization values, cookies, and common login
394
+ fields are redacted from client-generated error messages. The library does
395
+ not configure application logging.