diff --git a/.docs/README_template.rst b/.docs/README_template.rst index 4511f28..d027995 100644 --- a/.docs/README_template.rst +++ b/.docs/README_template.rst @@ -2,7 +2,7 @@ igittigitt ========== -Version v1.0.4 as of 2020-08-14 see `Changelog`_ +Version v1.0.5 as of 2020-08-14 see `Changelog`_ .. include:: ./badges.rst diff --git a/.docs/usage.rst b/.docs/usage.rst index 102552e..5ffcf1a 100644 --- a/.docs/usage.rst +++ b/.docs/usage.rst @@ -9,3 +9,15 @@ :code: python :start-after: # IgnoreParserExamples{{{ :end-before: # IgnoreParserExamples}}} + +- add a rule by string + +.. include:: ../igittigitt/igittigitt.py + :code: python + :start-after: # add_rule{{{ + :end-before: # add_rule}}} + +.. include:: ../tests/test_pytest.py + :code: python + :start-after: # add_rule_Example{{{ + :end-before: # add_rule_Example}}} diff --git a/CHANGES.rst b/CHANGES.rst index 977cbf4..a136454 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,10 @@ TODO: - documentation - asserts for __ALL__ parameters +v1.0.5 +-------- +2020-08-14: fix Windows and MacOs tests + v1.0.4 -------- 2020-08-13: handle trailing spaces diff --git a/README.rst b/README.rst index be79f49..3455a14 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ igittigitt ========== -Version v1.0.4 as of 2020-08-14 see `Changelog`_ +Version v1.0.5 as of 2020-08-14 see `Changelog`_ |travis_build| |license| |jupyter| |pypi| @@ -152,6 +152,28 @@ Usage ... print(parser) <...IgnoreParser object at ...> +- add a rule by string + +.. code-block:: python + + def add_rule(self, pattern: str, base_path: PathLikeOrString): + """ + add a rule as a string + + Parameter + --------- + pattern + the pattern + base_path + since gitignore patterns are relative to a base + directory, that needs to be provided here + """ + +.. code-block:: python + + >>> parser = igittigitt.IgnoreParser() + >>> parser.add_rule('*.py[cod]', base_path='/home/michael') + Usage from Commandline ------------------------ @@ -283,6 +305,10 @@ TODO: - documentation - asserts for __ALL__ parameters +v1.0.5 +-------- +2020-08-14: fix Windows and MacOs tests + v1.0.4 -------- 2020-08-13: handle trailing spaces diff --git a/igittigitt/__init__conf__.py b/igittigitt/__init__conf__.py index 7474f5d..40f5b76 100644 --- a/igittigitt/__init__conf__.py +++ b/igittigitt/__init__conf__.py @@ -1,6 +1,6 @@ name = 'igittigitt' title = 'A spec-compliant gitignore parser for Python' -version = 'v1.0.4' +version = 'v1.0.5' url = 'https://github.com/bitranox/igittigitt' author = 'Robert Nowotny' author_email = 'bitranox@gmail.com' @@ -14,7 +14,7 @@ def print_info() -> None: A spec-compliant gitignore parser for Python - Version : v1.0.4 + Version : v1.0.5 Url : https://github.com/bitranox/igittigitt Author : Robert Nowotny Email : bitranox@gmail.com""") diff --git a/igittigitt/igittigitt.py b/igittigitt/igittigitt.py index 9529bfa..b62a980 100644 --- a/igittigitt/igittigitt.py +++ b/igittigitt/igittigitt.py @@ -5,8 +5,11 @@ import re import sys from types import TracebackType -from typing import List, Optional, Tuple, Type +from typing import Any, List, Optional, Tuple, Type, Union +PathLikeOrString = Union[str, 'os.PathLike[Any]'] + +__all__ = ('IgnoreParser', ) whitespace_re = re.compile(r"(\\ )+$") @@ -75,7 +78,9 @@ def __exit__( pass def parse_rule_file( - self, path_rule_file: os.PathLike, base_dir: Optional[os.PathLike] = None + self, + rule_file: PathLikeOrString, + base_dir: Optional[PathLikeOrString] = None, ): """ parse a git ignore file, create rules from a gitignore file @@ -85,12 +90,28 @@ def parse_rule_file( full_path the full path to the ignore file base_dir - todo : good description missing + optional base dir, for testing purposes only. + the base dir is the parent of the rule file, + because rules are relative to the directory + were the rule file resides """ + if isinstance(rule_file, str): + path_rule_file = pathlib.Path(rule_file).resolve() + elif isinstance(rule_file, pathlib.Path): + path_rule_file = rule_file.resolve() + else: + raise TypeError('wrong type for "rule_file"') if base_dir is None: - base_dir = os.path.dirname(path_rule_file) + path_base_dir = path_rule_file.parent + elif isinstance(base_dir, str): + path_base_dir = pathlib.Path(base_dir).resolve() + elif isinstance(base_dir, pathlib.Path): + path_base_dir = base_dir.resolve() + else: + raise TypeError('wrong type for "base_dir"') + with open(path_rule_file) as ignore_file: counter = 0 for line in ignore_file: @@ -98,7 +119,7 @@ def parse_rule_file( line = line.rstrip("\n") rule = rule_from_pattern( line, - base_path=pathlib.Path(base_dir).resolve(), + base_path=path_base_dir, source=(path_rule_file, counter), ) if rule: @@ -106,8 +127,24 @@ def parse_rule_file( if rule.negation: self.rules_contains_negation_rule = True - def add_rule(self, pattern: str, base_path: Optional[os.PathLike] = None): - rule = rule_from_pattern(pattern, base_path) + # add_rule{{{ + def add_rule(self, pattern: str, base_path: PathLikeOrString): + """ + add a rule as a string + + Parameter + --------- + pattern + the pattern + base_path + since gitignore patterns are relative to a base + directory, that needs to be provided here + """ + # add_rule}}} + + path_base_path = pathlib.Path(base_path).resolve() + + rule = rule_from_pattern(pattern, path_base_path) if rule: self.rules.append(rule) if rule.negation: diff --git a/setup.py b/setup.py index 1648e8c..2ee15c4 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ def get_line_data(line: str) -> str: setup_kwargs: Dict[str, Any] = dict() setup_kwargs["name"] = "igittigitt" -setup_kwargs["version"] = "v1.0.4" +setup_kwargs["version"] = "v1.0.5" setup_kwargs["url"] = "https://github.com/bitranox/igittigitt" setup_kwargs["packages"] = find_packages() setup_kwargs["package_data"] = {"igittigitt": []} diff --git a/tests/test_pytest.py b/tests/test_pytest.py index 9dc8d48..8d670b0 100644 --- a/tests/test_pytest.py +++ b/tests/test_pytest.py @@ -1,5 +1,6 @@ # STDLIB import pathlib +import platform import pytest # PROJ @@ -7,155 +8,170 @@ @pytest.fixture(scope='function') -def parser_simple_git_rules(): +def base_path(): + if platform.system() == 'Windows': + return pathlib.Path('C:/Users/michael').resolve() + else: + return pathlib.Path('/home/michael').resolve() + + +@pytest.fixture(scope='function') +def parser_simple_git_rules(base_path): ignore_parser = igittigitt.IgnoreParser() - ignore_parser.add_rule('__pycache__', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('*.py[cod]', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('.venv/', base_path=pathlib.Path('/home/michael')) + # add rules with base path as pathlib.Path + ignore_parser.add_rule('__pycache__', base_path=base_path) + ignore_parser.add_rule('*.py[cod]', base_path=base_path) + ignore_parser.add_rule('.venv/', base_path=base_path) + # add rules with base path as string + ignore_parser.add_rule('__pycache__', base_path=str(base_path)) + ignore_parser.add_rule('*.py[cod]', base_path=str(base_path)) + ignore_parser.add_rule('.venv/', base_path=str(base_path)) + return ignore_parser @pytest.fixture(scope='function') -def parser_test_comments(): +def parser_test_comments(base_path): ignore_parser = igittigitt.IgnoreParser() - ignore_parser.add_rule('somematch', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('#realcomment', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('othermatch', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('\\#imnocomment', base_path=pathlib.Path('/home/michael')) + ignore_parser.add_rule('somematch', base_path=base_path) + ignore_parser.add_rule('#realcomment', base_path=base_path) + ignore_parser.add_rule('othermatch', base_path=base_path) + ignore_parser.add_rule('\\#imnocomment', base_path=base_path) return ignore_parser @pytest.fixture(scope='function') -def parser_test_wildcard(): +def parser_test_wildcard(base_path): ignore_parser = igittigitt.IgnoreParser() - ignore_parser.add_rule('hello.*', base_path=pathlib.Path('/home/michael')) + ignore_parser.add_rule('hello.*', base_path=base_path) return ignore_parser @pytest.fixture(scope='function') -def parser_test_anchored_wildcard(): +def parser_test_anchored_wildcard(base_path): ignore_parser = igittigitt.IgnoreParser() - ignore_parser.add_rule('/hello.*', base_path=pathlib.Path('/home/michael')) + ignore_parser.add_rule('/hello.*', base_path=base_path) return ignore_parser @pytest.fixture(scope='function') -def parser_test_trailing_spaces(): +def parser_test_trailing_spaces(base_path): """ is that really necessary ? Because there should be no files to filter with a trailing space on the filesystem ? """ ignore_parser = igittigitt.IgnoreParser() - ignore_parser.add_rule('ignoretrailingspace ', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('notignoredspace\\ ', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('partiallyignoredspace\\ ', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('partiallyignoredspace2 \\ ', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('notignoredmultiplespace\\ \\ \\ ', base_path=pathlib.Path('/home/michael')) + ignore_parser.add_rule('ignoretrailingspace ', base_path=base_path) + ignore_parser.add_rule('notignoredspace\\ ', base_path=base_path) + ignore_parser.add_rule('partiallyignoredspace\\ ', base_path=base_path) + ignore_parser.add_rule('partiallyignoredspace2 \\ ', base_path=base_path) + ignore_parser.add_rule('notignoredmultiplespace\\ \\ \\ ', base_path=base_path) return ignore_parser @pytest.fixture(scope='function') -def parser_negation_git_rules(): +def parser_negation_git_rules(base_path): ignore_parser = igittigitt.IgnoreParser() - ignore_parser.add_rule('*.ignore', base_path=pathlib.Path('/home/michael')) - ignore_parser.add_rule('!keep.ignore', base_path=pathlib.Path('/home/michael')) + ignore_parser.add_rule('*.ignore', base_path=base_path) + ignore_parser.add_rule('!keep.ignore', base_path=base_path) return ignore_parser -def test_simple_rules(parser_simple_git_rules): - assert not parser_simple_git_rules.match(pathlib.Path('/home/michael/main.py')) - assert not parser_simple_git_rules.match(pathlib.Path('/home/bitranox/main.py')) - assert parser_simple_git_rules.match(pathlib.Path('/home/michael/main.pyc')) - assert parser_simple_git_rules.match(pathlib.Path('/home/michael/dir/main.pyc')) - assert parser_simple_git_rules.match(pathlib.Path('/home/michael/__pycache__')) - assert parser_simple_git_rules.match(pathlib.Path('/home/michael/.venv/')) - assert parser_simple_git_rules.match(pathlib.Path('/home/michael/.venv/folder')) - assert parser_simple_git_rules.match(pathlib.Path('/home/michael/.venv/file.txt')) - - assert not parser_simple_git_rules.match('/home/michael/main.py') - assert not parser_simple_git_rules.match('/home/bitranox/main.py') - assert parser_simple_git_rules.match('/home/michael/main.pyc') - assert parser_simple_git_rules.match('/home/michael/dir/main.pyc') - assert parser_simple_git_rules.match('/home/michael/__pycache__') - assert parser_simple_git_rules.match('/home/michael/.venv/') - assert parser_simple_git_rules.match('/home/michael/.venv/folder') - assert parser_simple_git_rules.match('/home/michael/.venv/file.txt') - - -def test_comments(parser_test_comments): - assert parser_test_comments.match(pathlib.Path('/home/michael/somematch')) - assert not parser_test_comments.match(pathlib.Path('/home/michael/#realcomment')) - assert parser_test_comments.match(pathlib.Path('/home/michael/othermatch')) - assert parser_test_comments.match(pathlib.Path('/home/michael/#imnocomment')) - - assert parser_test_comments.match('/home/michael/somematch') - assert not parser_test_comments.match('/home/michael/#realcomment') - assert parser_test_comments.match('/home/michael/othermatch') - assert parser_test_comments.match('/home/michael/#imnocomment') - - -def test_wildcard(parser_test_wildcard): - assert parser_test_wildcard.match(pathlib.Path('/home/michael/hello.txt')) - assert parser_test_wildcard.match(pathlib.Path('/home/michael/hello.foobar/')) - assert parser_test_wildcard.match(pathlib.Path('/home/michael/dir/hello.txt')) - assert parser_test_wildcard.match(pathlib.Path('/home/michael/hello.')) - assert not parser_test_wildcard.match(pathlib.Path('/home/michael/hello')) - assert not parser_test_wildcard.match(pathlib.Path('/home/michael/helloX')) - - assert parser_test_wildcard.match('/home/michael/hello.txt') - assert parser_test_wildcard.match('/home/michael/hello.foobar/') - assert parser_test_wildcard.match('/home/michael/dir/hello.txt') - assert parser_test_wildcard.match('/home/michael/hello.') - assert not parser_test_wildcard.match('/home/michael/hello') - assert not parser_test_wildcard.match('/home/michael/helloX') - - -def test_anchored_wildcard(parser_test_anchored_wildcard): - assert parser_test_anchored_wildcard.match(pathlib.Path('/home/michael/hello.txt')) - assert parser_test_anchored_wildcard.match(pathlib.Path('/home/michael/hello.c')) - assert not parser_test_anchored_wildcard.match(pathlib.Path('/home/michael/a/hello.java')) - - assert parser_test_anchored_wildcard.match('/home/michael/hello.txt') - assert parser_test_anchored_wildcard.match('/home/michael/hello.c') - assert not parser_test_anchored_wildcard.match('/home/michael/a/hello.java') - - -def test_trailing_spaces(parser_test_trailing_spaces): +def test_simple_rules(parser_simple_git_rules, base_path): + assert not parser_simple_git_rules.match(base_path / 'main.py') + # test a path that is outside of the base path + assert not parser_simple_git_rules.match(base_path.parent / 'bitranox/main.py') + assert parser_simple_git_rules.match(base_path / 'main.pyc') + assert parser_simple_git_rules.match(base_path / 'dir/main.pyc') + assert parser_simple_git_rules.match(base_path / '__pycache__') + assert parser_simple_git_rules.match(base_path / '.venv/') + assert parser_simple_git_rules.match(base_path / '.venv/folder') + assert parser_simple_git_rules.match(base_path / '.venv/file.txt') + + assert not parser_simple_git_rules.match(str(base_path) + '/main.py') + assert not parser_simple_git_rules.match(str(base_path.parent) + '/bitranox/main.py') + assert parser_simple_git_rules.match(str(base_path) + '/main.pyc') + assert parser_simple_git_rules.match(str(base_path) + '/dir/main.pyc') + assert parser_simple_git_rules.match(str(base_path) + '/__pycache__') + assert parser_simple_git_rules.match(str(base_path) + '/.venv/') + assert parser_simple_git_rules.match(str(base_path) + '/.venv/folder') + assert parser_simple_git_rules.match(str(base_path) + '/.venv/file.txt') + + +def test_comments(parser_test_comments, base_path): + assert parser_test_comments.match(base_path / 'somematch') + assert not parser_test_comments.match(base_path / '#realcomment') + assert parser_test_comments.match(base_path / 'othermatch') + assert parser_test_comments.match(base_path / '#imnocomment') + + assert parser_test_comments.match(str(base_path) + '/somematch') + assert not parser_test_comments.match(str(base_path) + '/#realcomment') + assert parser_test_comments.match(str(base_path) + '/othermatch') + assert parser_test_comments.match(str(base_path) + '/#imnocomment') + + +def test_wildcard(parser_test_wildcard, base_path): + assert parser_test_wildcard.match(base_path / 'hello.txt') + assert parser_test_wildcard.match(base_path / 'hello.foobar/') + assert parser_test_wildcard.match(base_path / 'dir/hello.txt') + assert parser_test_wildcard.match(base_path / 'hello.') + assert not parser_test_wildcard.match(base_path / 'hello') + assert not parser_test_wildcard.match(base_path / 'helloX') + + assert parser_test_wildcard.match(str(base_path) + '/hello.txt') + assert parser_test_wildcard.match(str(base_path) + '/hello.foobar/') + assert parser_test_wildcard.match(str(base_path) + '/dir/hello.txt') + assert parser_test_wildcard.match(str(base_path) + '/hello.') + assert not parser_test_wildcard.match(str(base_path) + '/hello') + assert not parser_test_wildcard.match(str(base_path) + '/helloX') + + +def test_anchored_wildcard(parser_test_anchored_wildcard, base_path): + assert parser_test_anchored_wildcard.match(base_path / 'hello.txt') + assert parser_test_anchored_wildcard.match(base_path / 'hello.c') + assert not parser_test_anchored_wildcard.match(base_path / 'a/hello.java') + + assert parser_test_anchored_wildcard.match(str(base_path) + '/hello.txt') + assert parser_test_anchored_wildcard.match(str(base_path) + '/hello.c') + assert not parser_test_anchored_wildcard.match(str(base_path) + '/a/hello.java') + + +def test_trailing_spaces(parser_test_trailing_spaces, base_path): """ is that really necessary ? Because there should be no files to filter with a trailing space on the filesystem ? """ - assert parser_test_trailing_spaces.match('/home/michael/ignoretrailingspace') - assert not parser_test_trailing_spaces.match('/home/michael/ignoretrailingspace ') + assert parser_test_trailing_spaces.match(str(base_path) + '/ignoretrailingspace') + assert not parser_test_trailing_spaces.match(str(base_path) + '/ignoretrailingspace ') - assert parser_test_trailing_spaces.match('/home/michael/partiallyignoredspace ') - assert not parser_test_trailing_spaces.match('/home/michael/partiallyignoredspace ') - assert not parser_test_trailing_spaces.match('/home/michael/partiallyignoredspace') + assert parser_test_trailing_spaces.match(str(base_path) + '/partiallyignoredspace ') + assert not parser_test_trailing_spaces.match(str(base_path) + '/partiallyignoredspace ') + assert not parser_test_trailing_spaces.match(str(base_path) + '/partiallyignoredspace') - assert parser_test_trailing_spaces.match('/home/michael/partiallyignoredspace2 ') - assert not parser_test_trailing_spaces.match('/home/michael/partiallyignoredspace2 ') - assert not parser_test_trailing_spaces.match('/home/michael/partiallyignoredspace2 ') - assert not parser_test_trailing_spaces.match('/home/michael/partiallyignoredspace2') + assert parser_test_trailing_spaces.match(str(base_path) + '/partiallyignoredspace2 ') + assert not parser_test_trailing_spaces.match(str(base_path) + '/partiallyignoredspace2 ') + assert not parser_test_trailing_spaces.match(str(base_path) + '/partiallyignoredspace2 ') + assert not parser_test_trailing_spaces.match(str(base_path) + '/partiallyignoredspace2') - assert parser_test_trailing_spaces.match('/home/michael/notignoredspace ') - assert not parser_test_trailing_spaces.match('/home/michael/notignoredspace') + assert parser_test_trailing_spaces.match(str(base_path) + '/notignoredspace ') + assert not parser_test_trailing_spaces.match(str(base_path) + '/notignoredspace') - assert parser_test_trailing_spaces.match('/home/michael/notignoredmultiplespace ') - assert not parser_test_trailing_spaces.match('/home/michael/notignoredmultiplespace') + assert parser_test_trailing_spaces.match(str(base_path) + '/notignoredmultiplespace ') + assert not parser_test_trailing_spaces.match(str(base_path) + '/notignoredmultiplespace') -def test_negation_rules(parser_negation_git_rules): - assert parser_negation_git_rules.match(pathlib.Path('/home/michael/trash.ignore')) - assert parser_negation_git_rules.match(pathlib.Path('/home/michael/whatever.ignore')) - assert not parser_negation_git_rules.match(pathlib.Path('/home/michael/keep.ignore')) - assert not parser_negation_git_rules.match(pathlib.Path('/home/bitranox/keep.ignore')) +def test_negation_rules(parser_negation_git_rules, base_path): + assert parser_negation_git_rules.match(base_path / 'trash.ignore') + assert parser_negation_git_rules.match(base_path / 'whatever.ignore') + assert not parser_negation_git_rules.match(base_path / 'keep.ignore') + assert not parser_negation_git_rules.match(base_path.parent / 'bitranox/keep.ignore') - assert parser_negation_git_rules.match('/home/michael/trash.ignore') - assert parser_negation_git_rules.match('/home/michael/whatever.ignore') - assert not parser_negation_git_rules.match('/home/michael/keep.ignore') - assert not parser_negation_git_rules.match('/home/bitranox/keep.ignore') + assert parser_negation_git_rules.match(str(base_path) + '/trash.ignore') + assert parser_negation_git_rules.match(str(base_path) + '/whatever.ignore') + assert not parser_negation_git_rules.match(str(base_path) + '/keep.ignore') + assert not parser_negation_git_rules.match(str(base_path.parent) + '/bitranox/keep.ignore') def doctest_examples(): @@ -176,6 +192,13 @@ def doctest_examples(): # IgnoreParserExamples}}} + # add_rule_Example{{{ + >>> parser = igittigitt.IgnoreParser() + >>> parser.add_rule('*.py[cod]', base_path='/home/michael') + + # add_rule_Example}}} + + """ pass