
     i)                     T    S r SSKrSSKrSSKJrJr   " S S5      rS rS rS	 r	S
 r
g)z
Dirty Application Base Class

Provides the DirtyApp base class that all dirty applications must inherit from,
and utilities for loading dirty apps from import paths.
    N   )DirtyAppErrorDirtyAppNotFoundErrorc                   .    \ rS rSrSrSrS rS rS rSr	g)DirtyApp   a	  
Base class for dirty applications.

Dirty applications are loaded once when the dirty worker starts and
persist in memory for the lifetime of the worker. They are designed
for stateful resources like ML models, connection pools, etc.

Lifecycle
---------
1. ``__init__()``: Called when the app is instantiated (once per worker)
2. ``init()``: Called after instantiation to initialize resources
3. ``__call__()``: Called for each request from HTTP workers
4. ``close()``: Called when the worker shuts down

State Persistence
-----------------
Instance variables persist across requests. This is the key feature
that enables loading heavy resources once and reusing them::

    class MLApp(DirtyApp):
        def init(self):
            self.model = load_model()  # Loaded once, reused forever

        def predict(self, data):
            return self.model.predict(data)  # Same model for all requests

Thread Safety
-------------
With ``dirty_threads=1`` (default): Only one request runs at a time,
so no thread safety concerns.

With ``dirty_threads > 1``: Multiple requests may run concurrently
in the same worker. Your app MUST be thread-safe. Options:

- Use locks: ``threading.Lock()`` for shared state
- Use thread-local: ``threading.local()`` for per-thread state
- Use read-only state: Load models once in init(), never mutate

Example::

    import threading

    class ThreadSafeMLApp(DirtyApp):
        def __init__(self):
            self.models = {}
            self._lock = threading.Lock()

        def init(self):
            self.models['default'] = load_model('base-model')

        def load_model(self, name):
            with self._lock:
                if name not in self.models:
                    self.models[name] = load_model(name)
            return {"loaded": True, "name": name}

Worker Allocation
-----------------
By default, all dirty workers load all apps. For apps that consume
significant memory (like large ML models), you can limit how many
workers load the app by setting the ``workers`` class attribute::

    class HeavyModelApp(DirtyApp):
        workers = 2  # Only 2 workers will load this app

        def init(self):
            self.model = load_10gb_model()

Subclasses should implement:
    - init(): Called once at worker startup to initialize resources
    - __call__(action, *args, **kwargs): Handle requests from HTTP workers
    - close(): Called at worker shutdown to cleanup resources
Nc                     g)aB  
Initialize the application.

Called once when the dirty worker starts, after the app instance
is created. Use this for expensive initialization like loading
ML models, establishing database connections, etc.

This method is called in the child process after fork, so it's
safe to initialize non-fork-safe resources here.
N selfs    D/var/www/ias/venv/lib/python3.13/site-packages/gunicorn/dirty/app.pyinitDirtyApp.initb           c                 x    [        XS5      nUb  UR                  S5      (       a  [        SU 35      eU" U0 UD6$ )ao  
Handle a request from an HTTP worker.

Args:
    action: The action/method name to execute
    *args: Positional arguments for the action
    **kwargs: Keyword arguments for the action

Returns:
    The result of the action (must be JSON-serializable)

Raises:
    ValueError: If the action is unknown
    Any exception: Will be caught and returned as DirtyAppError
N_zUnknown action: )getattr
startswith
ValueError)r   actionargskwargsmethods        r   __call__DirtyApp.__call__n   sH      t,>V..s33/x899t&v&&r   c                     g)z
Cleanup resources.

Called when the dirty worker is shutting down. Use this to
release resources like database connections, unload models, etc.
Nr
   r   s    r   closeDirtyApp.close   r   r   r
   )
__name__
__module____qualname____firstlineno____doc__workersr   r   r   __static_attributes__r
   r   r   r   r      s    HZ G
'*r   r   c                 L   SU ;  a  [        SU  S3U S9eU R                  S5      n[        U5      S:X  a  U S4$ [        U5      S:X  a1  Uu  p#nU SU 3n [        U5      nUS:  a  [        SU  SU 3U S9eXV4$ [        SU  S3U S9e! [         a    [        SU  S	U S
3U S9ef = f)a  
Parse a dirty app specification.

Supports two formats:
- ``"module:Class"`` - standard format, all workers load the app
- ``"module:Class:N"`` - worker-limited format, only N workers load the app

Args:
    spec: The app specification string

Returns:
    tuple: (import_path, worker_count)
        - import_path: The "module:Class" part for importing
        - worker_count: Integer limit or None for all workers

Raises:
    DirtyAppError: If the spec format is invalid or worker_count is < 1

Examples::

    >>> parse_dirty_app_spec("myapp:App")
    ("myapp:App", None)

    >>> parse_dirty_app_spec("myapp:App:2")
    ("myapp:App", 2)

    >>> parse_dirty_app_spec("myapp.sub:App:1")
    ("myapp.sub:App", 1)
:Invalid import path format: z?. Expected 'module.path:ClassName' or 'module.path:ClassName:N'app_path   N   zInvalid worker count in spec: z. Expected integer, got ''r   z!. Worker count must be >= 1, got )r   splitlenintr   )specpartsmodule_path
class_name	count_strimport_pathworker_counts          r   parse_dirty_app_specr9      s#   < $*4& 1L M
 	
 JJsOE 5zQd| 5zQ-2*$Qzl3	y>L !0 722>A  ** 
&tf -H 	I #  	0 7**3A7 	s   B B#c                    SU ;  a  [        SU  S3U S9eU R                  SS5      u  p U[        R                  ;   a  [        R                  U   nO[        R
                  " U5      n  [        X25      n[        U[        5      (       d  [        U  S3U S9e U" 5       n/ S
QnU H;  n[        Xh5      (       a  [        [        Xh5      5      (       a  M.  [        U  SU 3U S9e   U$ ! [         a  n[        U 5      UeSnAff = f! [         a    [        U 5      Sef = f! [         a  n[        SU  S	U 3U S9UeSnAff = f)a+  
Load a dirty app class from an import path.

Args:
    import_path: String in format 'module.path:ClassName'

Returns:
    An instance of the dirty app class

Raises:
    DirtyAppNotFoundError: If the module or class cannot be found
    DirtyAppError: If the class is not a valid DirtyApp subclass
r(   r)   ". Expected 'module.path:ClassName'r*   r   N is not a classzFailed to instantiate z: )r   r   r   z is missing required method: )r   rsplitsysmodules	importlibimport_moduleImportErrorr   r   AttributeError
isinstancetype	Exceptionhasattrcallable)	r7   r4   r5   modulee	app_classapprequired_methodsmethod_names	            r   load_dirty_apprO      s|    +*;- 8/ 0 
 	
 *00a8K8#++%[[-F,,[9F
;F/	
 i&&m?+ 
 	
k 5's((9R0S0S-<[MJ$  ( JE  8#K0a78  ;#K0d:;  $[MA37 
 	sA   'C& C& *D D &
D0C<<DD
D>(D99D>c                 6    0 nU  H  n[        U5      X'   M     U$ )z
Load multiple dirty apps from a list of import paths.

Args:
    import_paths: List of import path strings

Returns:
    dict: Mapping of import path to app instance

Raises:
    DirtyAppError: If any app fails to load
)rO   )import_pathsappsr7   s      r   load_dirty_appsrS     s%     D#*;7 $Kr   c                    SU ;  a  [        SU  S3U S9eU R                  SS5      u  p U[        R                  ;   a  [        R                  U   nO[        R
                  " U5      n  [        X25      n[        U[        5      (       d  [        U  S3U S9e[        USS5      $ ! [         a  n[        U 5      UeSnAff = f! [         a    [        U 5      Sef = f)	a  
Get the workers class attribute from a dirty app without instantiating it.

This is used by the arbiter to determine how many workers should load
an app based on the class attribute, without needing to actually load
the app.

Args:
    import_path: String in format 'module.path:ClassName'

Returns:
    The workers class attribute value (int or None)

Raises:
    DirtyAppNotFoundError: If the module or class cannot be found
    DirtyAppError: If the import path format is invalid
r(   r)   r;   r*   r   Nr<   r%   )r   r=   r>   r?   r@   rA   rB   r   r   rC   rD   rE   )r7   r4   r5   rI   rJ   rK   s         r   get_app_workers_attributerU   ,  s    $ +*;- 8/ 0 
 	
 *00a8K8#++%[[-F,,[9F
;F/	
 i&&m?+ 
 	
 9i..#  8#K0a78  ;#K0d:;s)   'B$ B$ *C $
B?.B::B?C)r$   r@   r>   errorsr   r   r   r9   rO   rS   rU   r
   r   r   <module>rW      s9   
  
 8w wtHV?D&2/r   