Skip to content

Add support for file tags - #320

Open
loookashow wants to merge 7 commits into
mainfrom
feat/file-tags
Open

Add support for file tags#320
loookashow wants to merge 7 commits into
mainfrom
feat/file-tags

Conversation

@loookashow

Copy link
Copy Markdown
Contributor

Part 1 of 2, splitting #319 into reviewable pieces per @dmitry-mukhin's request. This is the file tags half; file search follows in a stacked PR based on this branch (search imports validate_tags from the tags module, so it has to land on top).

Exposes file tags from REST API v0.7. The Accept: application/vnd.uploadcare-v0.7+json header is already sent (conf.api_version is 0.7), so no header work was needed. Bumps the version to 6.3.0 (the search PR adds to the same changelog section without touching the version again).

What's included

  • TagsAPI (uploadcare.tags_api) mirroring MetadataAPI: get() / replace() / update(), covering GET / PUT / PATCH on /files/{uuid}/tags/.
  • On File: a tags property plus get_tags(), set_tags(), update_tags().
  • tags argument on Uploadcare.upload(), upload_files() and multipart_upload(), sent as the comma-separated tags form field.
  • tags on FileInfo.
  • New ucare commands get_file_tags, set_file_tags, update_file_tags, and --tags on ucare upload.
  • TagValidationError for tags exceeding the API limits (50 per file, 100 chars, Latin letters/digits/-/_/.).

Notable decisions

  • Tags are normalized (lowercase, trim, dedupe first-wins) before validation, so values the server would accept after its own normalization are not rejected locally.
  • TagsAPI.update() with no arguments sends an empty body — the endpoint documents both fields as optional. Requiring at least one of --add/--delete is a CLI-only concern. The 50-tag limit is not applied to PATCH delete, which is a candidate list where absent tags are ignored.
  • Uploads from url do not support tags upstream, so passing them raises InvalidParamError rather than silently dropping them.
  • FileInfo.model_dump() — and therefore File.info — now always contains a tags key (None when the endpoint does not report tags, [] for a file without tags).

Testing

  • Functional tests use unittest.mock for request shape (VCR matches on method + URI only) and hand-authored cassettes for response parsing.
  • Integration tests (tests/integration/test_file_tags.py, tests/integration/ucare_cli/test_file_tags.py) exercise the whole surface against the live API, creating and deleting their own files and uploading a PNG so they work on projects that restrict uploadable types.
  • One documentation correction from live verification: File.tags after a direct upload fetches the info and returns the stored tags (only multipart leaves it None).

make lint is clean; functional + Django suites pass (310 tests); the tags integration tests pass against a live project (21 tests).

🤖 Generated with Claude Code

Expose the REST API v0.7 file tags endpoints in the SDK.

- TagsAPI (uploadcare.tags_api) with get/replace/update, covering GET, PUT
  and PATCH on /files/{uuid}/tags/
- File: tags property plus get_tags(), set_tags() and update_tags()
- tags argument for Uploadcare.upload(), upload_files() and
  multipart_upload(), sent as the comma-separated tags form field
- tags in FileInfo
- ucare get_file_tags, set_file_tags and update_file_tags commands, and a
  --tags option for ucare upload
- TagValidationError for tags exceeding the API limits

Tags are normalized before validation, the way the API normalizes them, so
values it would accept after its own normalization are not rejected locally.

update() with no arguments sends an empty body, since the endpoint documents
both fields as optional; requiring at least one of --add/--delete is a CLI
concern instead. The 50-tags-per-file limit does not apply to PATCH delete,
which is a candidate list where absent tags are ignored.

Uploads from url do not support tags upstream, so passing them raises
InvalidParamError rather than dropping them silently.

FileInfo.model_dump(), and therefore File.info, now always contains a tags
key: None for responses that do not report tags, [] for files without any.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread docs/core_api.rst Outdated
Comment thread pyuploadcare/client.py Outdated
Comment thread pyuploadcare/client.py Outdated
Comment thread pyuploadcare/api/api.py
Comment thread pyuploadcare/resources/file.py Outdated
Comment on lines +323 to +331
"""Replaces all file tags by requesting Uploadcare API.

Passing an empty list clears the tags::

>>> file_ = uploadcare.file('a771f854-c2cb-408a-8c36-71af77811f3b')
>>> file_.set_tags(['cat', 'animal'])
UpdateFileTagsResponse(tags=['cat', 'animal'], added=['cat', 'animal'], deleted=[])

"""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is hard to parse.
so it clears the tags and adds the ones in arg?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 58d15f9, please check

Comment thread pyuploadcare/resources/file.py Outdated
Comment thread pyuploadcare/api/api.py Outdated
loookashow and others added 6 commits August 5, 2026 14:04
Co-authored-by: Dmitry Mukhin <dm@uploadcare.com>
Co-authored-by: Dmitry Mukhin <dm@uploadcare.com>
Co-authored-by: Dmitry Mukhin <dm@uploadcare.com>
Co-authored-by: Dmitry Mukhin <dm@uploadcare.com>
Co-authored-by: Dmitry Mukhin <dm@uploadcare.com>
The web edits renamed TagsAPI.replace to set but left File.set_tags, the
tag tests, and the internal response_classes key on the old name, which
broke File.set_tags at runtime. Point them all at set, and reword the
set_tags docstring to state it replaces the whole tag set.
@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants