o
    ;i                     @   s   d 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	m
Z
mZmZ ddlmZ G d	d
 d
ZG dd deZG dd deZed e dZe  e dZdeee e	egef f de	egef fddZdS )a  Pattern matching library ported from https://github.com/moby/patternmatcher.

This is the same pattern-matching logic used by Docker, except it is written in
Python rather than Go. Also, the original Go library has a couple deprecated
functions that we don't implement in this port.

The main way to use this library is by constructing a `FilePatternMatcher` object,
then asking it whether file paths match any of its patterns.
    N)abstractmethod)cached_property)Path)CallableOptionalSequenceUnion   )Patternc                       sp   e Zd ZU dZee ed< dddZdddZdef fdd	Z	e
defd
dZe
dedefddZ  ZS )_AbstractPatternMatcherN_custom_reprreturnc                    s   t  fddS )a\  Invert the filter. Returns a function that returns True if the path does not match any of the patterns.

        Usage:
        ```python
        from pathlib import Path
        from modal import FilePatternMatcher

        inverted_matcher = ~FilePatternMatcher("**/*.py")

        assert not inverted_matcher(Path("foo.py"))
        ```
        c                    s
    |  S N )pathselfr   N/home/ubuntu/.local/lib/python3.10/site-packages/modal/file_pattern_matcher.py<lambda>%   s   
 z4_AbstractPatternMatcher.__invert__.<locals>.<lambda>)_CustomPatternMatcherr   r   r   r   
__invert__   s   z"_AbstractPatternMatcher.__invert__c                 C   s
   || _ | S r   )r   )r   custom_reprr   r   r   
_with_repr'   s   z"_AbstractPatternMatcher._with_reprc                    s   | j r| j S t  S r   )r   super__repr__r   	__class__r   r   r   ,   s   
z _AbstractPatternMatcher.__repr__c                 C      dS )aD  
        Returns True if this pattern matcher allows safe early directory pruning.

        Directory pruning is safe when matching directories can be skipped entirely
        without missing any files that should be included.

        An example where pruning is not safe is for inverted patterns, like "!**/*.py".
        Nr   r   r   r   r   can_prune_directories2   s   
z-_AbstractPatternMatcher.can_prune_directoriesr   c                 C   s   d S r   r   r   r   r   r   r   __call__>   s   z _AbstractPatternMatcher.__call__)r   r   )__name__
__module____qualname__r   r   str__annotations__r   r   r   r   boolr   r   r    __classcell__r   r   r   r   r      s   
 

r   c                   @   sD   e Zd Zdeegef fddZdefddZdedefdd	Zd
S )r   	predicatec                 C   s
   || _ d S r   
_predicate)r   r(   r   r   r   __init__C      
z_CustomPatternMatcher.__init__r   c                 C   r   )z
        Custom pattern matchers (like negated matchers) cannot safely prune directories.

        Since these are arbitrary predicates, we cannot determine if a directory
        can be safely skipped without evaluating all files within it.
        Fr   r   r   r   r   r   F   s   z+_CustomPatternMatcher.can_prune_directoriesr   c                 C   s
   |  |S r   r)   r   r   r   r   r    O   r,   z_CustomPatternMatcher.__call__N)	r!   r"   r#   r   r   r&   r+   r   r    r   r   r   r   r   B   s    	r   c                   @   s   e Zd ZU dZeeeef  ed< ee	e  ed< de	e de
e fddZdedd	fd
dZedeeef dd fddZdedefddZede
e fddZdefddZdedefddZd	S )FilePatternMatcherax  
    Allows matching file Path objects against a list of patterns.

    **Usage:**
    ```python
    from pathlib import Path
    from modal import FilePatternMatcher

    matcher = FilePatternMatcher("*.py")

    assert matcher(Path("foo.py"))

    # You can also negate the matcher.
    negated_matcher = ~matcher

    assert not negated_matcher(Path("foo.py"))
    ```
    
_file_path_pattern_stringspatternsr   c                 C   s   g }t |D ]@}| tjj}|sqtj|}t }|d dkr6t|dkr-tdd|_	|dd  }||_
|tjj|_|| q|S )Nr   !r	   zIllegal exclusion pattern: "!"T)liststriposr   sepnormpathr
   len
ValueError	exclusioncleaned_patternsplitdirsappend)r   r0   parsed_patternspatternnew_patternr   r   r   _parse_patternsj   s    z"FilePatternMatcher._parse_patternsr?   Nc                 G   s   || _ d| _dS )zInitialize a new FilePatternMatcher instance.

        Args:
            pattern (str): One or more pattern strings.

        Raises:
            ValueError: If an illegal exclusion pattern is provided.
        N)r/   r.   )r   r?   r   r   r   r+   }   s   	
zFilePatternMatcher.__init__	file_pathc                 C   s   |  | }||_d|_|S )a  Initialize a new FilePatternMatcher instance from a file.

        The patterns in the file will be read lazily when the matcher is first used.

        Args:
            file_path (Path): The path to the file containing patterns.

        **Usage:**
        ```python
        from modal import FilePatternMatcher

        matcher = FilePatternMatcher.from_file("/path/to/ignorefile")
        ```

        N)__new__r.   r/   )clsrB   instancer   r   r   	from_file   s   
zFilePatternMatcher.from_filec           	      C   s   d}t j|}|dkrdS t j|}|dkrd}|t jj}| jD ]8}|j|kr,q$||}|sV|dkrVt	t
|D ]}t jj|d|d  }||rUd} nq=|r\|j }q$|S )a  Check if the file path or any of its parent directories match the patterns.

        This is equivalent to `MatchesOrParentMatches()` in the original Go
        library. The reason is that `Matches()` in the original library is
        deprecated due to buggy behavior.
        F. Nr	   T)r4   r   r6   dirnamer;   r5   r0   r9   matchranger7   join)	r   rB   matchedparent_pathparent_path_dirsr?   rJ   idir_pathr   r   r   _matches   s.   



zFilePatternMatcher._matchesc                 C   s4   | j durt| j d }nt| j}| |S )z1Get the patterns, loading from file if necessary.Nutf8)r.   r   	read_text
splitlinesr2   r/   rA   )r   pattern_stringsr   r   r   r0      s   


zFilePatternMatcher.patternsc                 C   s   t dd | jD  S )aQ  
        Returns True if this pattern matcher allows safe early directory pruning.

        Directory pruning is safe when matching directories can be skipped entirely
        without missing any files that should be included. This is for example not
        safe when we have inverted/negated ignore patterns (e.g. "!**/*.py").
        c                 s   s    | ]}|j V  qd S r   )r9   ).0r?   r   r   r   	<genexpr>   s    z;FilePatternMatcher.can_prune_directories.<locals>.<genexpr>)anyr0   r   r   r   r   r      s   z(FilePatternMatcher.can_prune_directoriesc                 C   s   |  t|S r   )rR   r$   )r   rB   r   r   r   r       s   zFilePatternMatcher.__call__)r!   r"   r#   __doc__r   r   r$   r   r%   r   r2   r
   rA   r+   classmethodrF   r&   rR   r   r0   r   r    r   r   r   r   r-   S   s   
 %
r-   z**/*.pyz.NON_PYTHON_FILESz	._NOTHINGignorer   c                 C   s   t | r| S t|  S r   )callabler-   )r\   r   r   r   
_ignore_fn   s   r^   )rZ   r4   abcr   	functoolsr   pathlibr   typingr   r   r   r   _utils.pattern_utilsr
   r   r   r-   r   r!   NON_PYTHON_FILES_NOTHINGr$   r&   r^   r   r   r   r   <module>   s   
- 6