Skip to content

Conversation

@sg-s
Copy link
Collaborator

@sg-s sg-s commented Oct 31, 2025

No description provided.

@sg-s sg-s requested a review from Copilot October 31, 2025 13:37
@sg-s sg-s merged commit 96784a7 into main Oct 31, 2025
5 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds a new list_files_in_dir method to the UFAClient class to support directory listing functionality. The implementation calls a UFA directory listing endpoint and returns paginated results with optional continuation tokens.

  • Adds list_files_in_dir method to UFAClient with support for pagination and result limits
  • Implements integration test to verify directory listing functionality
  • Adds documentation explaining the API usage and endpoint structure

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/ufa/client.py Adds list_files_in_dir method with percent-encoding of directory paths and pagination support
tests/test_client.py Adds integration test that uploads fixtures and verifies they appear in directory listing
docs/ufa_client.md Provides usage examples and documents the HTTP endpoint mapping

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

query_parts.append(f"maxKeys={max_keys}")
url = base if not query_parts else f"{base}?{'&'.join(query_parts)}"

async with httpx.AsyncClient() as client:
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

The list_files_in_dir method creates a new httpx.AsyncClient for each call without specifying a timeout, unlike upload_file which sets explicit timeouts. This could lead to indefinite hangs if the server is unresponsive. Consider adding a timeout configuration similar to the upload method.

Suggested change
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(timeout=httpx.Timeout(60.0, connect=10.0)) as client:

Copilot uses AI. Check for mistakes.
Comment on lines +271 to +281
async with httpx.AsyncClient() as client:
response = await self._make_request(client, url)
resp_ct = response.headers.get("content-type", "")
if "application/json" in resp_ct:
return response.json()
# Fallback: attempt to decode JSON even if content-type is missing
try:
return response.json()
except ValueError:
logger.error("Unexpected non-JSON response for directory listing")
raise UFAError(
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

The fallback logic at lines 277-278 is redundant. If the content-type check at line 274 passes, the method returns at line 275 and never reaches the fallback. If it fails, the try-except block will execute the same response.json() call. Consider simplifying to a single try-except block that handles both cases.

Copilot uses AI. Check for mistakes.
# Fallback: attempt to decode JSON even if content-type is missing
try:
return response.json()
except ValueError:
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

The exception handling catches ValueError, but response.json() can raise json.JSONDecodeError (which inherits from ValueError) or other exceptions. Consider explicitly catching json.JSONDecodeError for clarity, or handling potential httpx exceptions that could occur during JSON parsing.

Copilot uses AI. Check for mistakes.
Comment on lines +119 to +137
def test_list_files_in_dir(ufa_client: UFAClient) -> None:
"""Upload fixtures and verify they appear in directory listing."""
local_files = _iter_fixture_files()
if not local_files:
pytest.skip("no fixtures to upload")

# Ensure all fixtures are present remotely under REMOTE_PREFIX
for p in local_files:
asyncio.run(
ufa_client.upload_file(
local_file_path=str(p),
remote_file_path=_remote_path_for(p),
)
)

# List the directory and assert each filename is present
listing = asyncio.run(
ufa_client.list_files_in_dir(directory_path=REMOTE_PREFIX.rstrip("/"))
)
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

Multiple asyncio.run() calls in a loop create and tear down an event loop for each iteration. This is inefficient and could cause issues with async resource management. Consider using a single asyncio.run() call with asyncio.gather() to upload all files concurrently, or define the test as an async function and use pytest-asyncio.

Suggested change
def test_list_files_in_dir(ufa_client: UFAClient) -> None:
"""Upload fixtures and verify they appear in directory listing."""
local_files = _iter_fixture_files()
if not local_files:
pytest.skip("no fixtures to upload")
# Ensure all fixtures are present remotely under REMOTE_PREFIX
for p in local_files:
asyncio.run(
ufa_client.upload_file(
local_file_path=str(p),
remote_file_path=_remote_path_for(p),
)
)
# List the directory and assert each filename is present
listing = asyncio.run(
ufa_client.list_files_in_dir(directory_path=REMOTE_PREFIX.rstrip("/"))
)
@pytest.mark.asyncio
async def test_list_files_in_dir(ufa_client: UFAClient) -> None:
"""Upload fixtures and verify they appear in directory listing."""
local_files = _iter_fixture_files()
if not local_files:
pytest.skip("no fixtures to upload")
# Ensure all fixtures are present remotely under REMOTE_PREFIX
await asyncio.gather(*[
ufa_client.upload_file(
local_file_path=str(p),
remote_file_path=_remote_path_for(p),
)
for p in local_files
])
# List the directory and assert each filename is present
listing = await ufa_client.list_files_in_dir(directory_path=REMOTE_PREFIX.rstrip("/"))

Copilot uses AI. Check for mistakes.
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