CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-azure-storage-file-share

Azure File Share storage client library for Python

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

lease-client.mddocs/

ShareLeaseClient - Lease Management

The ShareLeaseClient provides lease management operations for files and shares, enabling exclusive access control through lease acquisition, renewal, release, and breaking operations.

Import and Initialization

from azure.storage.fileshare import ShareLeaseClient, ShareClient, ShareFileClient
from typing import Union, Any, Optional

Constructor

class ShareLeaseClient:
    def __init__(
        self,
        client: Union[ShareClient, ShareFileClient],
        lease_id: Optional[str] = None
    ) -> None:
        """
        Create a ShareLeaseClient for managing leases.
        
        Parameters:
            client: ShareClient or ShareFileClient to manage leases for
            lease_id: Optional existing lease ID (UUID format)
        """

Lease Operations

def acquire(
    self,
    **kwargs: Any
) -> None:
    """
    Requests a new lease for the file or share.
    
    Parameters:
        lease_duration: Duration of the lease in seconds (-1 for infinite, default: -1)
        timeout: Request timeout in seconds
        
    Note:
        - For files: lease_duration can be 15-60 seconds or -1 for infinite
        - For shares: lease_duration is always infinite (-1)
        - After acquisition, the lease_id property will contain the lease ID
    """

def renew(
    self,
    **kwargs: Any
) -> None:
    """
    Renews the lease for the file or share.
    
    Parameters:
        timeout: Request timeout in seconds
        
    Note:
        - Lease must be in 'leased' state to be renewed
        - Extends the lease for the same duration as originally acquired
    """

def release(
    self,
    **kwargs: Any
) -> None:
    """
    Releases the lease for the file or share.
    
    Parameters:
        timeout: Request timeout in seconds
        
    Note:
        - Makes the file/share available for other operations immediately
        - Lease cannot be renewed after release
    """

def change(
    self,
    proposed_lease_id: str,
    **kwargs: Any
) -> None:
    """
    Changes the lease ID of an active lease.
    
    Parameters:
        proposed_lease_id: New lease ID (UUID format)
        timeout: Request timeout in seconds
        
    Note:
        - Lease must be in 'leased' state to change ID
        - Updates the lease_id property to the new ID
    """

def break_lease(
    self,
    **kwargs: Any
) -> int:
    """
    Breaks the lease, if the file or share has an active lease.
    
    Parameters:
        lease_break_period: Time to wait before lease is broken (0-60 seconds)
        timeout: Request timeout in seconds
        
    Returns:
        int: Time remaining until lease is broken (seconds)
        
    Note:
        - If lease_break_period is not specified, lease breaks immediately
        - Returns 0 if lease is broken immediately
    """

Properties

@property
def id(self) -> str:
    """
    The lease ID for the lease.
    
    Returns:
        str: UUID string representing the lease ID
    """

@property
def etag(self) -> Optional[str]:
    """
    The ETag of the lease.
    
    Returns:
        Optional[str]: ETag from the last lease operation
    """

@property
def last_modified(self) -> Optional[datetime]:
    """
    The last modified timestamp of the lease.
    
    Returns:
        Optional[datetime]: Last modified time from the last lease operation
    """

Context Manager Support

def __enter__(self) -> ShareLeaseClient:
    """
    Enter context manager and acquire lease.
    
    Returns:
        ShareLeaseClient: Self for use in with statement
    """

def __exit__(self, exc_type, exc_val, exc_tb) -> None:
    """
    Exit context manager and release lease.
    
    Parameters:
        exc_type: Exception type if exception occurred
        exc_val: Exception value if exception occurred  
        exc_tb: Exception traceback if exception occurred
    """

Usage Examples

Basic Lease Operations

from azure.storage.fileshare import ShareFileClient, ShareLeaseClient
from azure.core.credentials import AzureNamedKeyCredential
import uuid

# Initialize file client
credential = AzureNamedKeyCredential("myaccount", "mykey")
file_client = ShareFileClient(
    account_url="https://myaccount.file.core.windows.net",
    share_name="documents",
    file_path="important_file.txt",
    credential=credential
)

# Acquire lease with specific ID
lease_id = str(uuid.uuid4())
lease_client = ShareLeaseClient(file_client, lease_id=lease_id)
lease_client.acquire(lease_duration=60)  # 60-second lease
print(f"Lease acquired: {lease_client.id}")

# Perform operations with lease
try:
    # File operations now require lease_id
    file_client.set_file_metadata(
        {"locked": "true", "locked_by": "process_123"},
        lease_id=lease_client.id
    )
    
    # Upload new content with lease protection
    file_client.upload_file(
        data=b"Updated content under lease",
        overwrite=True,
        lease_id=lease_client.id
    )
    
finally:
    # Always release the lease
    lease_client.release()
    print("Lease released")

Lease Management with Context Manager

# Automatic lease management using context manager
with ShareLeaseClient(file_client) as lease:
    print(f"Lease automatically acquired: {lease.id}")
    
    # Perform protected operations
    file_client.upload_file(
        data=b"Content updated with automatic lease management",
        overwrite=True,
        lease_id=lease.id
    )
    
    properties = file_client.get_file_properties()
    print(f"File size: {properties.size} bytes")
    
# Lease is automatically released when exiting context
print("Lease automatically released")

Share-Level Lease Management

from azure.storage.fileshare import ShareClient

# Initialize share client
share_client = ShareClient(
    account_url="https://myaccount.file.core.windows.net",
    share_name="protected-share",
    credential=credential
)

# Acquire infinite lease on share
share_lease = ShareLeaseClient(share_client)
share_lease.acquire()  # Share leases are always infinite
print(f"Share lease acquired: {share_lease.id}")

try:
    # Share operations require lease_id
    share_client.set_share_metadata(
        {"maintenance_mode": "true", "locked_by": "admin"},
        lease_id=share_lease.id
    )
    
    # Create directory with share lease
    directory_client = share_client.create_directory(
        "maintenance",
        lease_id=share_lease.id
    )
    
finally:
    share_lease.release()
    print("Share lease released")

Lease Renewal and Extension

import time

# Acquire short-term lease
lease_client = ShareLeaseClient(file_client)
lease_client.acquire(lease_duration=30)  # 30-second lease
print(f"Initial lease acquired: {lease_client.id}")

# Simulate long-running operation with lease renewal
for i in range(5):
    # Perform some work
    print(f"Processing step {i+1}...")
    time.sleep(10)
    
    # Renew lease before it expires
    try:
        lease_client.renew()
        print("Lease renewed successfully")
    except Exception as e:
        print(f"Failed to renew lease: {e}")
        break

lease_client.release()

Lease ID Management and Change

import uuid

# Start with specific lease ID
original_lease_id = str(uuid.uuid4())
lease_client = ShareLeaseClient(file_client, lease_id=original_lease_id)
lease_client.acquire(lease_duration=-1)  # Infinite lease
print(f"Original lease ID: {lease_client.id}")

# Change lease ID (useful for transferring lease ownership)
new_lease_id = str(uuid.uuid4())
lease_client.change(proposed_lease_id=new_lease_id)
print(f"Changed lease ID to: {lease_client.id}")

# Verify the change worked
assert lease_client.id == new_lease_id
print("Lease ID change confirmed")

# Operations now use new lease ID
file_client.set_file_metadata(
    {"lease_changed": "true"},
    lease_id=lease_client.id
)

lease_client.release()

Lease Breaking and Recovery

# Scenario: Another process needs to break an existing lease
lease_client = ShareLeaseClient(file_client)
lease_client.acquire(lease_duration=-1)  # Infinite lease
print(f"Lease acquired: {lease_client.id}")

# Simulate another process that needs to break the lease
breaker_lease = ShareLeaseClient(file_client)

# Break the lease with 30-second break period
remaining_time = breaker_lease.break_lease(lease_break_period=30)
print(f"Lease break initiated, {remaining_time} seconds remaining")

# Original lease operations will now fail
try:
    file_client.upload_file(
        data=b"This will fail",
        lease_id=lease_client.id
    )
except Exception as e:
    print(f"Operation failed as expected: {e}")

# Wait for lease to be broken or break immediately
if remaining_time > 0:
    print("Waiting for lease to break naturally...")
    time.sleep(remaining_time + 1)
else:
    print("Lease broken immediately")

# Now operations work without lease
file_client.upload_file(data=b"This works after lease break", overwrite=True)
print("Operation successful after lease break")

Error Handling and Lease States

from azure.core.exceptions import HttpResponseError

def safe_lease_operations(file_client):
    """Demonstrate safe lease handling with error recovery."""
    lease_client = ShareLeaseClient(file_client)
    
    try:
        # Attempt to acquire lease
        lease_client.acquire(lease_duration=60)
        print(f"Lease acquired successfully: {lease_client.id}")
        
        # Perform operations
        return perform_lease_operations(file_client, lease_client)
        
    except HttpResponseError as e:
        if e.error_code == "LeaseAlreadyPresent":
            print("File is already leased by another client")
            # Try to break existing lease
            existing_lease = ShareLeaseClient(file_client)
            existing_lease.break_lease(lease_break_period=0)  # Break immediately
            print("Broke existing lease, retrying...")
            
            # Retry acquisition
            lease_client.acquire(lease_duration=60)
            return perform_lease_operations(file_client, lease_client)
        else:
            print(f"Lease operation failed: {e.message}")
            raise
            
    except Exception as e:
        print(f"Unexpected error: {e}")
        raise
        
    finally:
        # Ensure lease is released
        try:
            lease_client.release()
            print("Lease released in cleanup")
        except:
            pass  # Ignore release errors in cleanup

def perform_lease_operations(file_client, lease_client):
    """Perform file operations under lease protection."""
    try:
        # Update file metadata
        file_client.set_file_metadata(
            {"processing": "true", "start_time": time.time()},
            lease_id=lease_client.id
        )
        
        # Simulate processing
        print("Performing protected operations...")
        time.sleep(2)
        
        # Update content
        file_client.upload_file(
            data=b"Processed content",
            overwrite=True,
            lease_id=lease_client.id
        )
        
        # Final metadata update
        file_client.set_file_metadata(
            {"processing": "complete", "end_time": time.time()},
            lease_id=lease_client.id
        )
        
        return True
        
    except HttpResponseError as e:
        if e.error_code in ["LeaseIdMissing", "LeaseIdMismatch", "LeaseNotPresent"]:
            print("Lease was lost during operation")
            return False
        raise

# Use the safe lease operations
success = safe_lease_operations(file_client)
print(f"Operation completed successfully: {success}")

Concurrent Access Control

import threading
import time

def worker_with_lease(worker_id, file_client, work_duration=5):
    """Worker function that tries to acquire lease and perform work."""
    print(f"Worker {worker_id}: Starting")
    
    lease_client = ShareLeaseClient(file_client)
    
    try:
        # Try to acquire lease
        lease_client.acquire(lease_duration=work_duration + 5)  # Extra buffer
        print(f"Worker {worker_id}: Lease acquired {lease_client.id}")
        
        # Perform exclusive work
        for i in range(work_duration):
            print(f"Worker {worker_id}: Working... step {i+1}")
            
            # Update file to show work progress
            file_client.set_file_metadata(
                {
                    "worker": str(worker_id),
                    "step": str(i+1),
                    "timestamp": str(time.time())
                },
                lease_id=lease_client.id
            )
            
            time.sleep(1)
        
        print(f"Worker {worker_id}: Work completed")
        
    except HttpResponseError as e:
        if e.error_code == "LeaseAlreadyPresent":
            print(f"Worker {worker_id}: File is locked by another worker")
        else:
            print(f"Worker {worker_id}: Error - {e.message}")
    
    except Exception as e:
        print(f"Worker {worker_id}: Unexpected error - {e}")
    
    finally:
        try:
            lease_client.release()
            print(f"Worker {worker_id}: Lease released")
        except:
            pass

# Start multiple workers (only one will get the lease)
threads = []
for i in range(3):
    thread = threading.Thread(target=worker_with_lease, args=(i, file_client))
    threads.append(thread)
    thread.start()

# Wait for all workers to complete
for thread in threads:
    thread.join()

print("All workers finished")

Lease Monitoring and Diagnostics

def monitor_lease_status(file_client, lease_client):
    """Monitor and report lease status."""
    properties = file_client.get_file_properties()
    lease_props = properties.lease
    
    print(f"Lease Status Report:")
    print(f"  Status: {lease_props.status}")  # locked/unlocked
    print(f"  State: {lease_props.state}")    # available/leased/expired/breaking/broken
    print(f"  Duration: {lease_props.duration}")  # infinite/fixed
    
    if lease_props.state == "leased":
        print(f"  Current lease ID: {lease_client.id}")
        print(f"  Lease ETag: {lease_client.etag}")
        print(f"  Last modified: {lease_client.last_modified}")
    
    return lease_props.state

# Demonstrate lease monitoring
lease_client = ShareLeaseClient(file_client)

# Before lease
print("Before acquiring lease:")
monitor_lease_status(file_client, lease_client)

# Acquire lease
lease_client.acquire(lease_duration=60)
print("\nAfter acquiring lease:")
state = monitor_lease_status(file_client, lease_client)

# Renew lease  
if state == "leased":
    lease_client.renew()
    print("\nAfter renewing lease:")
    monitor_lease_status(file_client, lease_client)

# Release lease
lease_client.release()
print("\nAfter releasing lease:")
monitor_lease_status(file_client, lease_client)

The ShareLeaseClient provides robust lease management capabilities for controlling exclusive access to files and shares, ensuring data consistency and preventing concurrent modification conflicts in multi-client scenarios.

Install with Tessl CLI

npx tessl i tessl/pypi-azure-storage-file-share

docs

async-clients.md

directory-client.md

file-client.md

index.md

lease-client.md

models.md

sas-generation.md

service-client.md

share-client.md

tile.json