Repository: mherrmann/gitignore_parser

Issue:
Unsafe string slicing in IgnoreRule.match method. It fails with Path type.
## Synopsis
After upgrade from `0.1.0` to `0.1.1` I faced an issue with existing test. Looks like there is unsafe operation with `abs_path` which supposed to be a string only. That's a wrong assuming, because most libraries support the both types: [str, Path].

Code impacted: https://github.com/mherrmann/gitignore_parser/blob/v0.1.1/gitignore_parser.py#L143

### Steps to reproduce
```python
import tempfile
from pathlib import Path

from gitignore_parser import parse_gitignore


def reproduce_issue():
    with tempfile.TemporaryDirectory() as git_dir:
        git_dir = Path(git_dir)
        gitignore_file_path = git_dir / ".gitignore"
        gitignore_file_path.write_text("file1\n!file2")
        matches = parse_gitignore(gitignore_file_path)

        assert matches(git_dir / "file1")
        assert not matches(git_dir / "file2")


if __name__ == '__main__':
    reproduce_issue()
```

### Observed behaviour

```
Traceback (most recent call last):
  File "/Users/kukusan2/Workspace/gitignore_issue/reproduce_issue.py", line 20, in <module>
    reproduce_issue()
  File "/Users/kukusan2/Workspace/gitignore_issue/reproduce_issue.py", line 15, in reproduce_issue
    assert matches(git_dir / "file1")
  File "/Users/kukusan2/Workspace/gitignore_issue/venv2/lib/python3.9/site-packages/gitignore_parser.py", line 36, in <lambda>
    return lambda file_path: handle_negation(file_path, rules)
  File "/Users/kukusan2/Workspace/gitignore_issue/venv2/lib/python3.9/site-packages/gitignore_parser.py", line 11, in handle_negation
    if rule.match(file_path):
  File "/Users/kukusan2/Workspace/gitignore_issue/venv2/lib/python3.9/site-packages/gitignore_parser.py", line 143, in match
    if self.negation and abs_path[-1] == '/':
TypeError: 'PosixPath' object is not subscriptable
```


Discussion:

Patch:
diff --git a/gitignore_parser.py b/gitignore_parser.py
index f78d86f..0c79c46 100644
--- a/gitignore_parser.py
+++ b/gitignore_parser.py
@@ -4,6 +4,7 @@ import re
 
 from os.path import dirname
 from pathlib import Path
+from typing import Union
 
 def handle_negation(file_path, rules):
     matched = False
@@ -132,7 +133,7 @@ class IgnoreRule(collections.namedtuple('IgnoreRule_', IGNORE_RULE_FIELDS)):
     def __repr__(self):
         return ''.join(['IgnoreRule(\'', self.pattern, '\')'])
 
-    def match(self, abs_path):
+    def match(self, abs_path: Union[str, Path]):
         matched = False
         if self.base_path:
             rel_path = str(Path(abs_path).resolve().relative_to(self.base_path))
@@ -140,7 +141,7 @@ class IgnoreRule(collections.namedtuple('IgnoreRule_', IGNORE_RULE_FIELDS)):
             rel_path = str(Path(abs_path))
         # Path() strips the trailing slash, so we need to preserve it
         # in case of directory-only negation
-        if self.negation and abs_path[-1] == '/':
+        if self.negation and type(abs_path) == str and abs_path[-1] == '/':
             rel_path += '/'
         if rel_path.startswith('./'):
             rel_path = rel_path[2:]


Test patch:
diff --git a/tests.py b/tests.py
index 1340401..2934841 100644
--- a/tests.py
+++ b/tests.py
@@ -1,4 +1,5 @@
 from unittest.mock import patch, mock_open
+from pathlib import Path
 
 from gitignore_parser import parse_gitignore
 
@@ -127,6 +128,10 @@ data/**
         self.assertTrue(matches('/home/michael/directory'))
         self.assertTrue(matches('/home/michael/directory-trailing/'))
 
+    def test_supports_path_type_argument(self):
+        matches = _parse_gitignore_string('file1\n!file2', fake_base_dir='/home/michael')
+        self.assertTrue(matches(Path('/home/michael/file1')))
+        self.assertFalse(matches(Path('/home/michael/file2')))
 
 def _parse_gitignore_string(data: str, fake_base_dir: str = None):
     with patch('builtins.open', mock_open(read_data=data)):