The login cache is shared by every mdrs process, but the refresh was guarded by a lock that only reaches inside one. Concurrent runs each sent the same refresh token, and a provider that rotates them accepts the first and refuses the rest. - hold a lock that spans processes across the whole read-refresh- write, checking cheaply first so ordinary requests never take it - write the cache through a temporary file: opening it for writing truncates it, and a reader landing in that window found it empty and threw the session away - take the lock for every write, not just the refresh, so a login running beside one cannot be silently reverted - wait for a busy lock on Windows rather than giving up after the ten attempts msvcrt allows, and retry the rename it refuses while a reader still holds the file open - bound the refresh request on its own, so a provider that goes quiet cannot hold the lock indefinitely - accept bare hostnames such as localhost, store URLs without the trailing slash, and join download paths through one helper
4.8 KiB
4.8 KiB
Changelog
All notable changes to this project will be documented in this file.
[Unreleased]
Fixed
- Serialised the token refresh across processes. Concurrent
mdrsinvocations shared one refresh token and each sent it, so a provider that rotates refresh tokens accepted the first and refused the rest. - Wrote the login cache through a temporary file so a reader can no longer catch it mid-truncation and discard the session.
- Joined the base URL and the API's relative path correctly in
ls --jsonoutput, which produced a doubled separator when the configured URL ended with one.
Changed
- Accepted bare hostnames such as
localhostinconfig create/config update, and stored the URL without its trailing slash, matching the Rust client so both can shareconfig.ini. - Bounded the token refresh request with its own timeout, so a provider that stops answering cannot hold the cross-process lock indefinitely.
- Breaking for embedders:
CacheInterfacenow requireslock()andreload(). A cache passed toMdrsClient.from_remote(..., cache=...)must provide both;InMemoryCacheimplements them as no-ops.
[1.3.18] - 2026-07-02
Added
- Added a comprehensive unit test suite in
tests/test_commands.pychecking registration, parsing, and execution flow of all 16 commands.
Refactored
- Abstracted configuration storage (introducing
ConfigInterface,InMemoryConfig, and updatingConfigFile) to enable dependency injection. - Modularized transfer operations (upload and download) to decouple them from the service layer.
- Decoupled commands from direct file system configurations and migrated all subcommands to use abstract config classes.
Changed
- Upgraded dependencies including
pydantic-settingsto2.14.2andpyrightto1.1.411.
Fixed
- Fixed duplicate
__all__definitions in package initialization filemdrsclient/__init__.pythat caused__version__export to be overwritten.
[1.3.17] - 2026-07-02
Refactored
- Decoupled core logic from CLI interface and introduced
MdrsClientservice layer to improve library portability. - Migrated all CLI commands to utilize
MdrsClientfor execution.
Added
- Abstract authentication state using
CacheInterfaceandInMemoryCache.
[1.3.16] - 2026-06-12
Fixed
- Retrieve the full
Folderobject fromFoldersApiinstead of using theFolderSimplereturned byfind_sub_folderwhen resolving DOI subfolders. This fixes a type checker error under the upgraded pyright and avoids a potential AttributeError at runtime due toFolderSimplelacking thepathattribute.
Changed
- Upgraded dependencies and bumped version to 1.3.16 in pyproject.toml.
[1.3.15] - 2026-05-01
Fixed
- Apply NFC normalization to filenames and folder names sent to the server.
[1.3.14] - 2026-04-17
Changed
- Simplified
config listcommand (removed-l/--longoption, always display URL). - Renamed
--quickoption to--quietforlssubcommand.
Added
- Added subcommand aliases for config commands (e.g.
lsalias for list,rmalias for delete). - Added
versioncommand.
[1.3.13] - 2025-07-02
Fixed
- Fixed pagination logic for the
file.listAPI.
[1.3.12] - 2025-05-20
Fixed
- Fixed bug where file downloading was skipped incorrectly when
-s/--skip-if-file-existsoption was present.
[1.3.11] - 2025-01-21
Fixed
- Follow-up fixes for User API specification changes.
[1.3.10] - 2024-12-23
Added
- Delete broken files and show a summary when a file download fails.
Changed
- Updated dependency libraries.
[1.3.9] - 2024-10-23
Fixed
- Fixed compatibility with Python 3.10.
[1.3.8] - 2024-09-18
Added
- Implemented
-s/--skip-if-file-existsoption fordownloadcommand.
Fixed
- Added exception handling for unexpected responses from the server.
[1.3.7] - 2024-07-22
Added
- Implemented
--excludeargument for download subcommand.
[1.3.6] - 2024-07-08
Added
- Support cancelling recursive downloads if downloading some files fails.
[1.3.5] - 2024-07-08
Added
- Added authorization token validation checks for file download operations.
Removed
- Removed unnecessary debug code.
[1.3.4] - 2024-07-04
Added
- Added some aliases for config sub command.
Fixed
- Fixed bug when uploading large files.
[1.3.3] - 2024-02-13
Added
- Implemented
-s/--skip-if-file-existsoption foruploadcommand.
[1.3.2] - 2024-02-09
Added
- Added
-uand-poptions to login command.
[1.3.1] - 2023-12-20
Fixed
- Fixed bug to resolve local files for recursive file upload.
[1.3.0] - 2023-12-18
Changed
- Removed debug comments.
[1.2.0] - 2023-10-04
Changed
- Follow-up recent specification changes about folder access level.
[1.1.1] - 2023-07-26
Changed
- Set destination folder name using name attribute of folder copy API.