
    7GiT                        d Z ddlZddlmZmZ ddlmZmZmZm	Z	 ddl
mZ ddlmZ dZdZd	Zd
ZdZ eee      ZdedededefdZdededefdZdededededdf
dZ	 d)dedede	deef   dededee   ddfdZ	 d)dededeeef   dededee   ddfdZdeddfdZd d ed!dddd"d"f	de	edef   dededee   d#edee   d$eeeef      d%eeeef      d&e	eef   d'edefd(Z y)*a  Deprecation wrapper and utilities for marking deprecated code.

This module provides the main @deprecated decorator for marking functions, methods,
and classes as deprecated while optionally forwarding calls to their replacements.

Key Components:
    - deprecated(): Main decorator for deprecation with automatic call forwarding
    - Warning templates for different deprecation scenarios
    - Internal helpers for argument mapping and warning management

Copyright (C) 2020-2023 Jiri Borovec <...>
    N)partialwraps)AnyCallableOptionalUnion)warn)!get_func_arguments_types_defaultszThe `%(source_name)s` was deprecated since v%(deprecated_in)s in favor of `%(target_path)s`. It will be removed in v%(remove_in)s.zThe `%(source_name)s` uses deprecated arguments: %(argument_map)s. They were deprecated since v%(deprecated_in)s and will be removed in v%(remove_in)s.z`%(old_arg)s` -> `%(new_arg)s`zdThe `%(source_name)s` was deprecated since v%(deprecated_in)s. It will be removed in v%(remove_in)s.zI
.. deprecated:: %(deprecated_in)s
   %(remove_text)s
   %(target_text)s
)categoryfuncfn_args	fn_kwargsreturnc                     |s|S t        |       }|D cg c]  }|d   	 }}|j                  t        t        ||                   |S c c}w )a  Convert positional arguments to keyword arguments using function signature.

    This helper function takes positional arguments and converts them to keyword
    arguments by matching them with parameter names from the function signature.
    This enables consistent argument handling in the deprecation wrapper.

    Args:
        func: Function whose signature provides parameter names.
        fn_args: Tuple of positional arguments passed to the function.
        fn_kwargs: Dictionary of keyword arguments already passed.

    Returns:
        Dictionary combining converted positional arguments and existing kwargs,
        where positional args are now mapped to their parameter names

    Example:
        >>> from pprint import pprint
        >>> def example_func(a, b, c=3): pass
        >>> pprint(_update_kwargs_with_args(example_func, (1, 2), {'c': 5}))
        {'a': 1, 'b': 2, 'c': 5}

    r   )r
   updatedictzip)r   r   r   func_arg_type_valarg	arg_namess         e/home/nelsen/Projects/HRI/fr-in-the-cloud/.venv/lib/python3.12/site-packages/deprecate/deprecation.py_update_kwargs_with_argsr   /   sT    . 9$?#45CQ5I5T#i123 6s   Ac                     t        |       }|D ci c]"  }|d   t        j                  k7  s|d   |d   $ }}t        t	        |j                               t	        |j                               z         S c c}w )aA  Merge function default values with provided keyword arguments.

    This helper fills in default parameter values from the function signature
    for any parameters not explicitly provided. Provided kwargs take precedence
    over defaults.

    Args:
        func: Function whose signature provides default parameter values
        fn_kwargs: Dictionary of keyword arguments provided by caller

    Returns:
        Dictionary with defaults merged with provided kwargs, where provided
        values override defaults

    Example:
        >>> from pprint import pprint
        >>> def example_func(a=1, b=2, c=3): pass
        >>> pprint(_update_kwargs_with_defaults(example_func, {'b': 20}))
        {'a': 1, 'b': 20, 'c': 3}

    .. note:
        Parameters without defaults (inspect._empty) are not included in the result.

       r   )r
   inspect_emptyr   listitems)r   r   r   r   fn_defaultss        r   _update_kwargs_with_defaultsr    P   sm    2 :$?->[c#a&GNNBZ3q63q6>[K[[&&()D1B,CCDD \s
   A5A5streamsourcetemplate_mgsextrasc                     |j                   dk(  r|j                  j                  d      d   n|j                   }|j                   d| }t	        d||d|} | ||z         y)a  Issue a deprecation warning using the specified stream and message template.

    This is the core warning issuer that formats and emits deprecation warnings.
    It extracts source function metadata and combines it with provided template
    variables to generate the final warning message.

    Args:
        stream: Callable that outputs the warning (e.g., warnings.warn, logging.warning)
        source: The deprecated function/method being wrapped
        template_mgs: Python format string with placeholders for message variables
        **extras: Additional string values to substitute into the template
            (e.g., deprecated_in="1.0", remove_in="2.0")

    .. note:
        Automatically extracts source_name and source_path from the source callable:
        - For regular functions: uses __name__
        - For __init__ methods: extracts class name from __qualname__

    Example:
        >>> import warnings
        >>> def old_func(): pass
        >>> _raise_warn(
        ...     warnings.warn,
        ...     old_func,
        ...     "%(source_name)s deprecated in %(version)s",
        ...     version="1.0"
        ... )

    __init__.)source_namesource_pathN )__name____qualname__split
__module__r   )r!   r"   r#   r$   r)   r*   msg_argss          r   _raise_warnr1   o   sk    < 9?:8U&%%++C04[a[j[jK&&'q6KOOOH
<("#    targetdeprecated_in	remove_inc           	          t        |      r(|j                  }|j                   d| }|xs t        }nd\  }}|xs t        }t        | ||||||       y)am  Issue deprecation warning for callable (function/class) deprecation.

    This specialized warning issuer handles deprecation of entire functions or
    classes that are being replaced by new implementations. It automatically
    determines the appropriate message template based on whether a target
    callable is specified.

    Args:
        stream: Callable that outputs the warning (e.g., warnings.warn, logging.warning)
        source: The deprecated function/method being wrapped
        target: The replacement implementation:
            - Callable: Forward to this function/class
            - None: No forwarding (warning only mode)
            - bool: Not applicable for this function (use _raise_warn_arguments instead)
        deprecated_in: Version when the source was marked deprecated (e.g., "1.0.0")
        remove_in: Version when the source will be removed (e.g., "2.0.0")
        template_mgs: Custom message template. If None, uses default template based
            on whether target is callable or None

    Template Variables Available:
        - source_name: Function name (e.g., "old_func")
        - source_path: Full path (e.g., "mymodule.old_func")
        - target_name: Target function name (only if target is callable)
        - target_path: Full target path (only if target is callable)
        - deprecated_in: Version parameter value
        - remove_in: Version parameter value

    Example:
        >>> import warnings
        >>> def new_func(): pass
        >>> def old_func(): pass
        >>> _raise_warn_callable(
        ...     stream=warnings.warn,
        ...     source=old_func,
        ...     target=new_func,
        ...     deprecated_in="1.0",
        ...     remove_in="2.0"
        ... )
        >>> # Outputs: "The `old_func` was deprecated since v1.0 in favor of
        >>> #           `__main__.new_func`. It will be removed in v2.0."

    r'   ) r7   )r!   r"   r#   r4   r5   target_nametarget_pathN)callabler,   r/   TEMPLATE_WARNING_CALLABLETEMPLATE_WARNING_NO_TARGETr1   )r!   r"   r3   r4   r5   r#   r8   r9   s           r   _raise_warn_callabler=      sj    d oo**+1[M:#@'@#) [#A'A!#r2   	argumentsc           
          dj                  |j                         D cg c]  \  }}t        ||dz   c}}      }|xs t        }t	        | |||||       yc c}}w )a8  Issue deprecation warning for deprecated function arguments.

    This specialized warning issuer handles deprecation of specific function
    parameters that are being renamed or removed. It generates a mapping
    string showing the old-to-new argument names.

    Args:
        stream: Callable that outputs the warning (e.g., warnings.warn, logging.warning)
        source: The function/method whose arguments are deprecated
        arguments: Mapping from deprecated argument names to new names
            (e.g., {'old_arg': 'new_arg', 'removed_arg': None})
        deprecated_in: Version when arguments were marked deprecated (e.g., "1.0.0")
        remove_in: Version when arguments will be removed (e.g., "2.0.0")
        template_mgs: Custom message template. If None, uses default template

    Template Variables Available:
        - source_name: Function name (e.g., "my_func")
        - source_path: Full path (e.g., "mymodule.my_func")
        - argument_map: Formatted string showing mappings (e.g., "`old` -> `new`")
        - deprecated_in: Version parameter value
        - remove_in: Version parameter value

    Example:
        >>> import warnings
        >>> def my_func(old_arg=1, new_arg=1): pass
        >>> _raise_warn_arguments(
        ...     warnings.warn,
        ...     my_func,
        ...     {'old_arg': 'new_arg'},
        ...     "1.0",
        ...     "2.0"
        ... )
        >>> # Outputs: "The `my_func` uses deprecated arguments: `old_arg` -> `new_arg`.
        >>> #           They were deprecated since v1.0 and will be removed in v2.0."

    z, )old_argnew_arg)r4   r5   argument_mapN)joinr   TEMPLATE_ARGUMENT_MAPPINGTEMPLATE_WARNING_ARGUMENTSr1   )	r!   r"   r>   r4   r5   r#   abargs_maps	            r   _raise_warn_argumentsrI      s`    X yy_h_n_n_pqW[WXZ[3!PQ6RRqrH=#=LMU^muv rs   A

wrapped_fnc                    t        | d      r| j                  sy| j                  j                         }t        | di       }|j	                  dd      }|j	                  d      }|rd| dnd}t        |      rd	|j                   d|j                   d
nd}|j                  t        |j	                  dd      ||dz         dj                  |      | _        y)a  Append deprecation notice to function's docstring in reStructuredText format.

    This helper automatically generates and appends a Sphinx-compatible deprecation
    notice to the wrapped function's docstring. The notice includes version information
    and target replacement (if applicable), making it visible in generated API documentation.

    The appended notice follows the Sphinx deprecated directive format:
        .. deprecated:: <version>
           Will be removed in <version>.
           Use `<target>` instead.

    Args:
        wrapped_fn: Function whose docstring should be updated. Must have
            __deprecated__ attribute set with deprecation metadata

    Returns:
        None. Modifies the function's __doc__ attribute in-place.

    Metadata Used:
        The function's __deprecated__ attribute should contain:
        - deprecated_in: Version when deprecated
        - remove_in: Version when will be removed
        - target: Replacement callable (optional)

    Example:
        >>> def new_func(): pass
        >>> def old_func():
        ...     '''Original docstring.'''
        ...     pass
        >>> old_func.__deprecated__ = {
        ...     'deprecated_in': '1.0',
        ...     'remove_in': '2.0',
        ...     'target': new_func
        ... }
        >>> _update_docstring_with_deprecation(old_func)
        >>> print(old_func.__doc__) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
        Original docstring.
        <BLANKLINE>
        .. deprecated:: 1.0
           Will be removed in 2.0.
           Use `deprecate.deprecation.new_func` instead.

    .. note:
        Does nothing if the function has no docstring or no __deprecated__ attribute.

    __doc__N__deprecated__r5   r7   r3   zWill be removed in r'   zUse `z
` instead.r4   )r4   remove_texttarget_text
)hasattrrL   
splitlinesgetattrgetr:   r/   r,   appendTEMPLATE_DOC_DEPRECATEDrC   )rJ   linesdep_inforemove_in_val
target_valrN   rO   s          r   "_update_docstring_with_deprecationr[     s    ^ :y)1C1C))+Ez#3R8HLLb1Mh'J<I'a8rKU]^hUiE*//0*2E2E1FjQoqK	LL%\\/2>&&
	
 5)Jr2   r7      F	num_warnsargs_mapping
args_extraskip_ifupdate_docstringc
                 P    	 dt         dt         f 	f
d}
|
S )a  Decorate a function or class with warning message and forward calls to target.

    This decorator marks a function or class as deprecated and can automatically forward
    all calls to a replacement implementation. It supports argument mapping, custom
    warning messages, and flexible warning control.

    Args:
        target: How to handle the deprecation:
            - ``Callable``: Forward all calls to this function/class
            - ``True``: Self-deprecation mode (deprecate arguments within same function)
            - ``None``: Warning-only mode (no forwarding, function body executes normally)
        deprecated_in: Version when the function was deprecated (e.g., "1.0.0").
        remove_in: Version when the function will be removed (e.g., "2.0.0").
        stream: Function to output warnings (default: FutureWarning).
            Set to ``None`` to disable warnings entirely.
        num_warns: Number of times to show warning per function call:
            - ``1`` (default): Show warning once per function
            - ``-1``: Show warning on every call
            - ``N``: Show warning N times
        template_mgs: Custom warning message template with format specifiers:
            - ``source_name``: Function name (e.g., "my_func")
            - ``source_path``: Full path (e.g., "module.my_func")
            - ``target_name``: Target function name
            - ``target_path``: Full target path
            - ``deprecated_in``: Value of deprecated_in parameter
            - ``remove_in``: Value of remove_in parameter
            - ``argument_map``: String showing argument mapping (for args deprecation)
            Example: ``"v%(deprecated_in)s: `%(source_name)s` was deprecated."``
        args_mapping: Map or skip arguments when forwarding:
            - ``{'old_arg': 'new_arg'}``: Rename argument
            - ``{'old_arg': None}``: Skip argument (don't forward it)
            Works with both ``target=Callable`` and ``target=True``.
        args_extra: Additional arguments to pass to target function.
            Example: ``{'new_required_arg': 42}``
        skip_if: Conditionally skip deprecation:
            - ``bool``: Static condition
            - ``Callable``: Function returning bool (checked at runtime)
            If True/returns True, no warning is shown and original function executes.
        update_docstring: If True, automatically append deprecation information to
            the function's docstring. Useful for documentation generation tools.

    Returns:
        Decorator function that wraps the source function/class.

    Raises:
        TypeError: If arguments in args_mapping don't exist in target function
            and target doesn't accept **kwargs.

    Example:
        >>> # Basic forwarding
        >>> def new_func(x: int) -> int:
        ...     return x * 2
        >>> @deprecated(target=new_func, deprecated_in="1.0", remove_in="2.0")
        ... def old_func(x: int) -> int:
        ...     pass

        >>> # Argument mapping::
        >>> @deprecated(
        ...     target=new_func,
        ...     args_mapping={'old_name': 'new_name', 'unused': None}
        ... )
        ... def old_func(old_name: int, unused: str) -> int:
        ...     pass

        >>> # Self-deprecation::
        >>> @deprecated(target=True, args_mapping={'old_arg': 'new_arg'})
        ... def my_func(old_arg: int = 0, new_arg: int = 0) -> int:
        ...     return new_arg * 2

    r"   r   c                    
  t               dt        dt        dt        f 	
fd       t        d	d       rt               S )Nargskwargsr   c            
      p   t              r        n
t              }t        |t              st        dt	        |             |r | i |S t        dd      }t        d|dz          t        | |      }d u xs t              }i }r*r(j                         D ci c]  \  }}||v s|| }}}|s
|s d	i |S |r)|D cg c]  }t        d| d       }	}t        |	      }
nt        dd      }
rsdk  s|
k  ri|r!t               t        d|
dz          nF|rDt        |       |D cg c]  }d| 	 }}|D ]  }t        |t        |d      dz           |rt        |      }rOrMD cg c]
  }|   r	| }}|j                         D ci c]  \  }}||vsj                  ||      | }}}rr|j                         t              s d	i |S t        j                         rj"                  n}t%        |      D cg c]  }|d   	 }}t        j&                  |      }|j(                  }|D cg c]	  }||vs| }}|r|t        dj*                   d|        |d	i |S c c}}w c c}w c c}w c c}w c c}}w c c}w c c}w )
Nz7User function `shall_skip` shall return bool, but got: _calledr   r\   _warned__warnedzFailed mapping of `z'`, arguments missing in target source: r+   )r:   bool
isinstance	TypeErrortyperS   setattrr   r   minr=   rI   r    rT   r   r   isclassr&   r
   getfullargspecvarkwr,   )rd   re   
shall_skip	nb_calledreason_callablereason_argumentrF   rG   r   	arg_warns	nb_warnedattrib_namesn	args_skipvaltarget_functarget_argstarget_full_arg_specrr   missedr_   r^   r4   r]   r5   r`   r"   r!   r3   r#   rJ   s                       r   rJ   z/deprecated.<locals>.packing.<locals>.wrapped_fn  s    '/w&7T']Jj$/"YZ^_iZjYk lmmt.v..
Iq9IJ	9q=9-fdFCF$n@0@O O4@4F4F4H"XDAqAQWK1a4"X"X# '''  Raa#WZ8C51A1Ea	a	N	 $J	1=	 9q=I	,A"(PY[ghJ	9q=A$)&&/=Zceqr@O#Phse$4#PL#P) N
Awz1a/H1/LMN 5ffE,8RSS@QSR	R KQ,,.qhc3\_gp\p,**34c9qqfj)F#''' .5__V-D&//&K-N{-[\c3q6\K\ $+#9#9+#F (..E &,Fcs+/EcFFF%-"5foo5FFmntmu vww(((u #Y b $Q S r ] GsB   $J1JJJ
J#J#,J(9J()J.	J3&J3rM   )r4   r5   r3   r^   )r   r   rn   r[   )r"   rJ   r_   r^   r4   r]   r5   r`   r!   r3   r#   ra   s   `@r   packingzdeprecated.<locals>.packing  sn    	vK	)c K	)S K	)S K	) K	) 
K	)\ 	!.&  ,			
 .z:r2   )r   )r3   r4   r5   r!   r]   r#   r^   r_   r`   ra   r   s   `````````` r   
deprecatedr   J  s)    f^ ^X ^ ^@ Nr2   )N)!rL   r   	functoolsr   r   typingr   r   r   r   warningsr	   deprecate.utilsr
   r;   rE   rD   r<   rV   FutureWarningdeprecation_warningtupler   r   r    strr1   rj   r=   rI   r[   intr   r+   r2   r   <module>r      ss    $ 1 1  =- \ 
 =  k   d]; 8 e  QU BEx ED ET E>!$ !$( !$# !$QT !$Y] !$T #'AAA $h&'A 	A
 A 3-A 
AT #'.w.w.w CH~.w 	.w
 .w 3-.w 
.wb?*8 ?* ?*H !4"&-1+/%*"s$h&'ss s X	s
 s 3-s 4S>*s c3h(s 4>"s s sr2   