B
    ܇\+                 @   s   d Z ddlmZ ddlmZ ddlm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ZddlZddlmZ ddlmZ ed	Zed
d Zdd Zdd Zdd ZG dd deZe ZG dd deZdS )z, monkeypatching and mocking functionality.      )absolute_import)division)print_functionN)contextmanager)fixture)Pathz^No module named (.*)$c              c   s   t  } | V  |   dS )a  The returned ``monkeypatch`` fixture provides these
    helper methods to modify objects, dictionaries or os.environ::

        monkeypatch.setattr(obj, name, value, raising=True)
        monkeypatch.delattr(obj, name, raising=True)
        monkeypatch.setitem(mapping, name, value)
        monkeypatch.delitem(obj, name, raising=True)
        monkeypatch.setenv(name, value, prepend=False)
        monkeypatch.delenv(name, raising=True)
        monkeypatch.syspath_prepend(path)
        monkeypatch.chdir(path)

    All modifications will be undone after the requesting
    test function or fixture has finished. The ``raising``
    parameter determines if a KeyError or AttributeError
    will be raised if the set/deletion operation has no target.
    N)MonkeyPatchundo)Zmpatch r
   2lib/python3.7/site-packages/_pytest/monkeypatch.pymonkeypatch   s    r   c             C   s   |  d}|d}t|}x|D ]}|d| 7 }yt||}W q" tk
rT   Y nX q"yt| W nN tk
r } z0t|  d }||kr ntd||f W d d }~X Y nX t|||}q"W |S )N.r   zimport error in %s: %s)splitpop
__import__getattrAttributeErrorImportErrorstrannotated_getattr)namepartsZusedfoundpartexZexpectedr
   r
   r   resolve-   s&    


"r   c             C   s@   yt | |} W n, tk
r:   tdt| j||f Y nX | S )Nz#%r object at %s has no attribute %r)r   r   type__name__)objr   annr
   r
   r   r   J   s    r   c             C   sT   t | tjrd| kr"td| f | dd\}}t|}|rLt|||d ||fS )Nr   z+must be absolute import path string, not %r   )r    )
isinstancesixstring_types	TypeErrorrsplitr   r   )Zimport_pathraisingmoduleattrtargetr
   r
   r   derive_importpathT   s    r+   c               @   s   e Zd Zdd ZdS )Notsetc             C   s   dS )Nz<notset>r
   )selfr
   r
   r   __repr___   s    zNotset.__repr__N)r   
__module____qualname__r.   r
   r
   r
   r   r,   ^   s   r,   c               @   s   e Zd ZdZdd Zedd ZedfddZedfd	d
Z	dd Z
dddZdd ZdddZdddZdd Zdd Zdd ZdS )r   zj Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes.
    c             C   s   g | _ g | _d | _d | _d S )N)_setattr_setitem_cwd_savesyspath)r-   r
   r
   r   __init__j   s    zMonkeyPatch.__init__c             c   s    t  }z
|V  W d|  X dS )a  
        Context manager that returns a new :class:`MonkeyPatch` object which
        undoes any patching done inside the ``with`` block upon exit:

        .. code-block:: python

            import functools
            def test_partial(monkeypatch):
                with monkeypatch.context() as m:
                    m.setattr(functools, "partial", 3)

        Useful in situations where it is desired to undo some patches before the test ends,
        such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples
        of this see `#3290 <https://github.com/pytest-dev/pytest/issues/3290>`_.
        N)r   r	   )r-   mr
   r
   r   contextp   s    
zMonkeyPatch.contextTc             C   s   d}ddl }|tkr:t|tjs(td|}t||\}}t||t}|rb|tkrbtd||f |	|rz|j
|t}| j|||f t||| dS )ai   Set attribute value on target, memorizing the old value.
        By default raise AttributeError if the attribute did not exist.

        For convenience you can specify a string as ``target`` which
        will be interpreted as a dotted import path, with the last part
        being the attribute name.  Example:
        ``monkeypatch.setattr("os.getcwd", lambda: "/")``
        would set the ``getcwd`` function of the ``os`` module.

        The ``raising`` value determines if the setattr should fail
        if the attribute is not already present (defaults to True
        which means it will raise).
        Tr   Nzcuse setattr(target, name, value) or setattr(target, value) with target being a dotted import stringz%r has no attribute %r)inspectnotsetr"   r#   r$   r%   r+   r   r   isclass__dict__getr1   appendsetattr)r-   r*   r   valuer'   __tracebackhide__r8   oldvalr
   r
   r   r>      s    
zMonkeyPatch.setattrc             C   s   d}ddl }|tkr6t|tjs(tdt||\}}t||sN|rt|n@t	||t}|
|rr|j|t}| j|||f t|| dS )a   Delete attribute ``name`` from ``target``, by default raise
        AttributeError it the attribute did not previously exist.

        If no ``name`` is specified and ``target`` is a string
        it will be interpreted as a dotted import path with the
        last part being the attribute name.

        If ``raising`` is set to False, no exception will be raised if the
        attribute is missing.
        Tr   NzUuse delattr(target, name) or delattr(target) with target being a dotted import string)r8   r9   r"   r#   r$   r%   r+   hasattrr   r   r:   r;   r<   r1   r=   delattr)r-   r*   r   r'   r@   r8   rA   r
   r
   r   rC      s    


zMonkeyPatch.delattrc             C   s&   | j ||||tf |||< dS )z) Set dictionary entry ``name`` to value. N)r2   r=   r<   r9   )r-   dicr   r?   r
   r
   r   setitem   s    zMonkeyPatch.setitemc             C   s:   ||kr|r6t |n | j||||tf ||= dS )z Delete ``name`` from dict. Raise KeyError if it doesn't exist.

        If ``raising`` is set to False, no exception will be raised if the
        key is missing.
        N)KeyErrorr2   r=   r<   r9   )r-   rD   r   r'   r
   r
   r   delitem   s
    
zMonkeyPatch.delitemc             C   s*   t jr&t|ts&ttd| dS )zTOn Python 2, warn if the given environment variable name is not a native str (#4056)z,Environment variable name {!r} should be strN)	r#   ZPY2r"   r   warningswarnpytestPytestWarningformat)r-   r   r
   r
   r   _warn_if_env_name_is_not_str   s    z(MonkeyPatch._warn_if_env_name_is_not_strNc             C   sv   t |ts8tjtdj||t|jddd t|}|rX|t	j
krX|| t	j
|  }| | | t	j
|| dS )z Set environment variable ``name`` to ``value``.  If ``prepend``
        is a character, read the current environment variable value
        and prepend the ``value`` adjoined with the ``prepend`` character.zvValue of environment variable {name} type should be str, but got {value!r} (type: {type}); converted to str implicitly)r   r?   r      )
stacklevelN)r"   r   rH   rI   rJ   rK   rL   r   r   osenvironrM   rE   )r-   r   r?   Zprependr
   r
   r   setenv   s    

zMonkeyPatch.setenvc             C   s    |  | | jtj||d dS )z Delete ``name`` from the environment. Raise KeyError if it does
        not exist.

        If ``raising`` is set to False, no exception will be raised if the
        environment variable is missing.
        )r'   N)rM   rG   rP   rQ   )r-   r   r'   r
   r
   r   delenv   s    
zMonkeyPatch.delenvc             C   s0   | j dkrtjdd | _ tjdt| dS )z< Prepend ``path`` to ``sys.path`` list of import locations. Nr   )r4   syspathinsertr   )r-   rU   r
   r
   r   syspath_prepend  s    
zMonkeyPatch.syspath_prependc             C   sP   | j dkrt | _ t|dr(|  n$t|trBtt| n
t| dS )z} Change the current working directory to the specified path.
        Path can be a string or a py.path.local object.
        Nchdir)r3   rP   getcwdrB   rX   r"   r   r   )r-   rU   r
   r
   r   rX     s    




zMonkeyPatch.chdirc          	   C   s   x8t | jD ]*\}}}|tk	r,t||| qt|| qW g | jdd< xJt | jD ]<\}}}|tkry
||= W q tk
r   Y qX qT|||< qTW g | jdd< | jdk	r| jtj	dd< d| _| j
dk	rt| j
 d| _
dS )aE   Undo previous changes.  This call consumes the
        undo stack. Calling it a second time has no effect unless
        you do more monkeypatching after the undo call.

        There is generally no need to call `undo()`, since it is
        called automatically during tear-down.

        Note that the same `monkeypatch` fixture is used across a
        single test function invocation. If `monkeypatch` is used both by
        the test function itself and one of the test fixtures,
        calling `undo()` will undo all of the changes made in
        both functions.
        N)reversedr1   r9   r>   rC   r2   rF   r4   rT   rU   r3   rP   rX   )r-   r   r   r?   Z
dictionaryr
   r
   r   r	     s&    


zMonkeyPatch.undo)T)N)T)r   r/   r0   __doc__r5   r   r7   r9   r>   rC   rE   rG   rM   rR   rS   rW   rX   r	   r
   r
   r
   r   r   f   s   %"
	


r   )r[   Z
__future__r   r   r   rP   rerT   rH   
contextlibr   r#   rJ   Z_pytest.fixturesr   Z_pytest.pathlibr   compileZRE_IMPORT_ERROR_NAMEr   r   r   r+   objectr,   r9   r   r
   r
   r
   r   <module>   s(   


