aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Jon Bernard <jbernard@jbernard.io> 2018-10-09 13:04:03 -0400
committerGravatar Jon Bernard <jbernard@jbernard.io> 2019-03-05 10:16:06 -0500
commitb3e7538dc223ce06e999c771a2ff6f1052370677 (patch)
tree618cc5cf972a98e28bd6511f56c3ad878aa3ea0a
parent59d276bbfd84bf256dcaee6447f692ca4f5cfeab (diff)
downloaddotfiles-b3e7538dc223ce06e999c771a2ff6f1052370677.tar.gz
dotfiles-b3e7538dc223ce06e999c771a2ff6f1052370677.tar.bz2
dotfiles-b3e7538dc223ce06e999c771a2ff6f1052370677.zip
Finish repository class conversion to pathlib
-rw-r--r--dotfiles/repository.py38
-rw-r--r--tests/test_repository.py111
2 files changed, 94 insertions, 55 deletions
diff --git a/dotfiles/repository.py b/dotfiles/repository.py
index a29c142..ac0be2c 100644
--- a/dotfiles/repository.py
+++ b/dotfiles/repository.py
@@ -1,5 +1,8 @@
+import os
+
from click import echo
from pathlib import Path
+from fnmatch import fnmatch
from operator import attrgetter
from .dotfile import Dotfile
@@ -62,10 +65,8 @@ class Repository(object):
return '<Repository %r>' % str(self.path)
def _ignore(self, path):
- if not path.is_file():
- return True
for pattern in self.ignore_patterns:
- if path.match(pattern):
+ if fnmatch(path, '*/%s' % pattern):
return True
return False
@@ -96,20 +97,24 @@ class Repository(object):
target = self._dotfile_target(path)
- if not path.fnmatch('%s/*' % self.homedir):
+ if not fnmatch(path, '%s/*' % self.homedir):
raise NotRootedInHome(path)
- if path.fnmatch('%s/*' % self.path):
+ if fnmatch(path, '%s/*' % self.path):
raise InRepository(path)
if self._ignore(target):
raise TargetIgnored(path)
- if path.check(dir=1):
+ if path.is_dir():
raise IsDirectory(path)
return Dotfile(path, target)
def _contents(self, dir):
"""Return all unignored files contained below a directory."""
- return [x for x in dir.rglob('*') if not self._ignore(x)]
+
+ def skip(path):
+ return path.is_dir() or self._ignore(path)
+
+ return [x for x in dir.rglob('*') if not skip(x)]
def contents(self):
"""Return a list of dotfiles for each file in the repository."""
@@ -133,7 +138,7 @@ class Repository(object):
paths = list(set(map(Path, paths)))
for path in paths:
- if path.check(dir=1):
+ if path.is_dir():
paths.extend(self._contents(path))
paths.remove(path)
@@ -146,7 +151,7 @@ class Repository(object):
return [d for d in map(construct, paths) if d is not None]
- def prune(self):
+ def prune(self, debug=False):
"""Remove any empty directories in the repository.
After a remove operation, there may be empty directories remaining.
@@ -154,9 +159,14 @@ class Repository(object):
so pruning must take place explicitly after such operations occur.
"""
- def filter(node):
- return node.check(dir=1) and not self._ignore(node)
+ def skip(path):
+ return self._ignore(path) or path == str(self.path)
+
+ dirs = reversed([dir for dir, subdirs, files in
+ os.walk(self.path) if not skip(dir)])
- for dir in self.path.visit(filter, lambda x: not self._ignore(x)):
- if not len(dir.listdir()):
- dir.remove()
+ for dir in dirs:
+ if not len(os.listdir(dir)):
+ if debug:
+ echo('PRUNE %s' % (dir))
+ os.rmdir(dir)
diff --git a/tests/test_repository.py b/tests/test_repository.py
index ed8543c..cdf3c72 100644
--- a/tests/test_repository.py
+++ b/tests/test_repository.py
@@ -1,7 +1,8 @@
import pytest
from pathlib import Path
-from dotfiles.exceptions import NotRootedInHome
+from dotfiles.exceptions import NotRootedInHome, TargetIgnored, \
+ IsDirectory, InRepository
from dotfiles.repository import Repository, \
REMOVE_LEADING_DOT, IGNORE_PATTERNS
@@ -88,45 +89,73 @@ def test_dotfile_target(repo, path):
def test_dotfile(repo):
+
with pytest.raises(NotRootedInHome):
repo._dotfile(Path('/tmp/foo'))
- # with pytest.raises(TargetIgnored):
- # repo.ignore_patterns = ['.foo']
- # repo.remove_leading_dot = False
- # repo._dotfile(py.path.local(repo.homedir.join('.foo')))
- # repo._dotfile(repo.homedir / '.foo')
-
-# with pytest.raises(TargetIgnored):
-# repo.ignore_patterns = ['foo']
-# repo._dotfile(repo.homedir.join('.bar/foo'))
-# with pytest.raises(IsDirectory):
-# repo._dotfile(repo.homedir.ensure_dir('.config'))
-
-# # The repo fixture is parametrized, we can only expect InRepository
-# # exception when the repository is contained in the home directory.
-# if repo.path.dirname == repo.homedir.basename:
-# with pytest.raises(InRepository):
-# repo._dotfile(repo.path.join('.foo/bar'))
-
-# repo._dotfile(repo.homedir.join('.foo'))
-
-
-# def test_dotfiles(repo):
-# file = repo.homedir.join('.baz')
-# dir = repo.homedir.ensure_dir('.dir')
-# dir.ensure('foo/bat')
-# dir.ensure('foo/buz')
-# dir.ensure('bar')
-# dir.ensure('boo')
-
-# dotfiles = repo.dotfiles([str(file), str(dir)])
-# assert len(dotfiles) == 5
-
-
-# def test_prune(repo):
-# repo.path.ensure_dir('.a/a')
-# repo.path.ensure_dir('.b/b/b/b')
-# repo.path.ensure_dir('.c/c/c/c/c/c/c/c')
-
-# repo.prune()
-# assert len(repo.path.listdir()) == 0
+
+ with pytest.raises(TargetIgnored):
+ repo.ignore_patterns = ['.foo']
+ repo.remove_leading_dot = False
+ repo._dotfile(repo.homedir / '.foo')
+
+ with pytest.raises(TargetIgnored):
+ repo.ignore_patterns = ['foo']
+ repo._dotfile(repo.homedir / '.bar/foo')
+
+ with pytest.raises(IsDirectory):
+ dir = repo.homedir / '.config'
+ dir.mkdir()
+ repo._dotfile(dir)
+
+ # The repo fixture is parametrized, we can only expect InRepository
+ # exception when the repository is contained in the home directory.
+ if repo.path.parent == repo.homedir.name:
+ with pytest.raises(InRepository):
+ repo._dotfile(repo.path / '.foo/bar')
+
+
+def test_dotfiles(repo):
+
+ subdir_a = repo.homedir / '.dir'
+ subdir_b = repo.homedir / '.dir/foo'
+
+ for subdir in [subdir_a, subdir_b]:
+ subdir.mkdir()
+
+ file_a = repo.homedir / '.baz'
+ file_b = subdir_a / 'bar'
+ file_c = subdir_a / 'boo'
+ file_d = subdir_b / 'bat'
+ file_e = subdir_b / 'buz'
+
+ for file in [file_a, file_b, file_c, file_d, file_e]:
+ file.touch()
+
+ dotfiles = repo.dotfiles([str(file_a), str(subdir_a)])
+
+ assert len(dotfiles) == 5
+
+ for file in [file_a, file_b, file_c, file_d, file_e]:
+ assert str(file) in map(str, dotfiles)
+
+
+def test_prune(repo):
+ dir_a = repo.path / '.a/a'
+ dir_b = repo.path / '.b/b/b/b'
+ dir_c = repo.path / '.c/c/c/c/c/c/c/c'
+
+ for dir in [dir_a, dir_b, dir_c]:
+ dir.mkdir(parents=True)
+
+ repo.prune()
+ contents = [x for x in repo.path.rglob('*')]
+ assert len(contents) == 0
+
+ # verify ignored directories are excluded from pruning
+ dir_d = repo.path / '.git'
+ dir_d.mkdir()
+
+ repo.prune()
+ contents = [x for x in repo.path.rglob('*')]
+ assert str(dir_d) in map(str, contents)
+ assert len(contents) == 1