o
    i.                     @   s   d dl Z d dlZd dlZd dlZd dlZd dlZd dlmZ d dlm	Z	 ddl
mZ ddlmZ G dd	 d	ZG d
d dZdededB fddZdedB fddZdedB fddZe ZeeadS )    N)RLock)Optional   )logger   )SageMakerConfigc                   @   sb   e Zd ZdZ	ddededee fddZdefd	d
ZddefddZ	dd Z
defddZdS )SessionzRepresents a single stateful session with file-based storage.

    Each session has a unique directory where it stores key-value data
    as JSON files. Sessions have an expiration timestamp and are automatically
    cleaned up when expired.
    N
session_idsession_rootexpiration_tsc                 C   s:   || _ tj||| _|| _| jdu r| d| _dS dS )a*  Initialize a session instance.

        Args:
            session_id: Unique identifier for this session (typically UUID)
            session_root: Root directory where session directories are stored
            expiration_ts: Unix timestamp when session expires, or None to load from disk
        N.expiration_ts)r	   ospathjoin
files_pathr   get)selfr	   r
   r    r   p/home/ubuntu/.local/lib/python3.10/site-packages/model_hosting_container_standards/sagemaker/sessions/manager.py__init__   s   

zSession.__init__keyc                 C   sB   t | |d}t|| W d   dS 1 sw   Y  dS )a  Store a JSON-serializable value in the session.

        Args:
            key: The key to store the value under
            value: Must be JSON-serializable (str, int, float, bool, list, dict, None)

        Raises:
            TypeError: If value is not JSON-serializable
        wN)open_pathjsondump)r   r   valuefr   r   r   put*   s   
"zSession.putc                 C   sR   |  |}tj|s|S t|d}t|W  d   S 1 s"w   Y  dS )zRetrieve a value from the session.

        Args:
            key: The key to retrieve
            d: Default value if key doesn't exist

        Returns:
            The stored value or default if key not found
        rN)r   r   r   isfiler   r   load)r   r   dr   r   r   r   r   r   7   s   

$zSession.getc                 C   s@   t j| jstd| j td| j  t	| j dS )zDelete the session and all its stored data from disk.

        Returns:
            bool: True if deletion successful

        Raises:
            ValueError: If session directory doesn't exist
        z"session directory does not exist: zclosing session: T)
r   r   existsr   
ValueErrorr	   r   infoshutilrmtree)r   r   r   r   removeH   s
   	zSession.removec                 C   s   d|v rt d| dtj|rt d| d|dd}tj| j|}tj|}|tj| jtj	 sCt d| d|S )a-  Generate a safe file path within the session directory.

        Args:
            key: The key name for the session data file

        Returns:
            Absolute path to the file within the session directory

        Raises:
            ValueError: If key contains path traversal attempts
        z..z&Invalid key: '..' not allowed in key ''z)Invalid key: absolute paths not allowed '/-z&Invalid key: path traversal detected ')
r$   r   r   isabsreplacer   r   abspath
startswithsep)r   r   sanitized_key	file_pathresolved_pathr   r   r   r   W   s   zSession._path)N)__name__
__module____qualname____doc__strr   floatr   r   r   r(   r   r   r   r   r   r      s    
r   c                   @   sR   e Zd ZdZdefddZdefddZdede	e fd	d
Z
dd Zdd ZdS )SessionManagera2  Manages the lifecycle of stateful sessions with automatic expiration and cleanup.

    SessionManager maintains a registry of active sessions, each stored in its own
    directory on disk. It handles session creation, retrieval, expiration checking,
    and cleanup. Thread-safe for concurrent access.
    
propertiesc              
   C   s  t |dtd| _|d}|du rBtjt d}tj	do,t
dtjtjB }|r1dn|}td|r:d	nd
 d|  || _i | _t | _z9tj| jdd t
| jtjtjB shtd| j td| j  t| jD ]}t|| j| j|< qwW dS  ttfy } z/td| j d| d tjt d| _tj| jdd td| j  W Y d}~dS d}~ww )aQ  Initialize the SessionManager with configuration properties.

        Args:
            properties: Configuration dict with optional keys:
                - sessions_expiration: Session lifetime in seconds (default: 1200)
                - sessions_path: Root directory for session storage (default: /dev/shm/sagemaker_sessions)
        sessions_expirationi  sessions_pathNsagemaker_sessionsz/dev/shmz/dev/shm/sagemaker_sessionsz$Sessions path not configured, using zshared memoryztemp directoryz: T)exist_okzCannot write to z Session storage initialized at: z!Failed to initialize sessions at z . Falling back to temp directoryz)Session storage initialized at fallback: )intr   r8   
expirationr   r   r   tempfile
gettempdirr#   accessR_OKW_OKr   r%   r=   sessionsr   _lockmakedirsPermissionErrorlistdirr   OSErrorwarning)r   r;   r=   temp_sessions_pathshm_accessibler	   er   r   r   r   }   sL   	


zSessionManager.__init__returnc                 C   s   | j 3 |   tt }t | j }t|| j|}|| j	|< t
|j |d| |W  d   S 1 s9w   Y  dS )a>  Create a new session with a unique ID and expiration timestamp.

        Also triggers cleanup of any expired sessions before creating the new one.

        Returns:
            Session: The newly created session instance with UUID and expiration

        Thread-safe: Uses internal lock for concurrent access
        r   N)rH   _clean_expired_sessionr8   uuiduuid4timerA   r   r=   rG   r   rI   r   r   )r   r	   r   sessionr   r   r   create_session   s   

$zSessionManager.create_sessionr	   c                 C   s   | j L |dks
|s	 W d   dS || jvrtd| | j| }|jdurFt |jkrFtd|  | | 	 W d   dS |W  d   S 1 sRw   Y  dS )a  Retrieve a session by ID, checking for expiration.

        Args:
            session_id: The unique session identifier

        Returns:
            Session instance if found and not expired, None if session doesn't exist
            or has expired, or if session_id is "NEW_SESSION" or empty

        Raises:
            ValueError: If session_id is not found in registry

        Thread-safe: Uses internal lock for concurrent access
        NEW_SESSIONNsession not found: zSession expired: )rH   rG   r$   r   rU   r   r%   close_sessionr   r	   rV   r   r   r   get_session   s   



$zSessionManager.get_sessionc                 C   sp   | j + |std| || jvrtd| | j| }|  | j|= W d   dS 1 s1w   Y  dS )a&  Close and remove a session, deleting all its data.

        Args:
            session_id: The unique session identifier to close

        Raises:
            ValueError: If session_id is empty/None or not found in registry

        Thread-safe: Uses internal lock for concurrent access
        zinvalid session_id: rY   N)rH   r$   rG   r(   r[   r   r   r   rZ      s   


"zSessionManager.close_sessionc                 C   sf   | j & t| j D ]\}}|jdu st |jkr | | qW d   dS 1 s,w   Y  dS )a  Internal method to remove all expired sessions.

        Iterates through all sessions and closes any that have expired.
        Called automatically during session creation to prevent stale session buildup.

        Thread-safe: Uses internal lock for concurrent access
        N)rH   listrG   itemsr   rU   rZ   r[   r   r   r   rR     s   
"z%SessionManager._clean_expired_sessionN)r4   r5   r6   r7   dictr   r   rW   r8   r   r\   rZ   rR   r   r   r   r   r:   u   s    9"r:   configrQ   c                 C   s$   | j rt| j| jd}t|S dS )zInitialize a SessionManager if stateful sessions are enabled.

    Args:
        config: SagemakerConfig instance with session settings

    Returns:
        SessionManager instance if enabled, None otherwise
    )r<   r=   N)enable_stateful_sessionsr8   r<   r=   r:   )r`   config_dictr   r   r   _init_session_manager  s   	rc   c                   C   s   t S )zGet the global session manager instance.

    Returns:
        The global SessionManager instance, or None if not initialized
    )session_managerr   r   r   r   get_session_manager,  s   re   c                  C   s   t  } t| atS )a  Initialize the global session manager from environment variables.

    This can be called to reinitialize the session manager after environment
    variables have been set.

    Returns:
        The initialized SessionManager instance, or None if disabled
    )r   from_envrc   rd   )r`   r   r   r   init_session_manager_from_env5  s   
rg   )r   r   r&   rB   rU   rS   	threadingr   typingr   logging_configr   r`   r   r   r:   rc   re   rg   rf   _configrd   r   r   r   r   <module>   s$   d %	