aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dotfiles/__init__.py2
-rw-r--r--dotfiles/dotfile.py5
-rw-r--r--dotfiles/repository.py66
-rw-r--r--setup.py17
-rw-r--r--tests/test_repository.py169
5 files changed, 132 insertions, 127 deletions
diff --git a/dotfiles/__init__.py b/dotfiles/__init__.py
index 2f762b6..df5cb58 100644
--- a/dotfiles/__init__.py
+++ b/dotfiles/__init__.py
@@ -9,4 +9,4 @@
:license: ISC, see LICENSE.md for more details.
"""
-__version__ = '0.9.dev0'
+__version__ = '0.9.dev1'
diff --git a/dotfiles/dotfile.py b/dotfiles/dotfile.py
index d59f07b..b17822e 100644
--- a/dotfiles/dotfile.py
+++ b/dotfiles/dotfile.py
@@ -1,5 +1,5 @@
-import py.path
from click import echo
+from pathlib import Path
from .exceptions import \
IsSymlink, NotASymlink, TargetExists, TargetMissing, Exists
@@ -59,7 +59,8 @@ class Dotfile(object):
def short_name(self, homedir):
"""A shorter, more readable name given a home directory."""
- return homedir.bestrelpath(self.name)
+ return self.name.relative_to(homedir)
+ # return homedir.bestrelpath(self.name)
def is_present(self):
"""Is this dotfile present in the repository?"""
diff --git a/dotfiles/repository.py b/dotfiles/repository.py
index 85a963d..474c9ba 100644
--- a/dotfiles/repository.py
+++ b/dotfiles/repository.py
@@ -1,15 +1,15 @@
-import click
-import py.path
+from click import echo
+from pathlib import Path
from operator import attrgetter
from .dotfile import Dotfile
from .exceptions import DotfileException, TargetIgnored
from .exceptions import NotRootedInHome, InRepository, IsDirectory
-DEFAULT_PATH = '~/Dotfiles'
-DEFAULT_REMOVE_LEADING_DOT = True
-DEFAULT_IGNORE_PATTERNS = ['.git', '.gitignore', 'README*', '*~']
-DEFAULT_HOMEDIR = py.path.local('~/', expanduser=True)
+PATH = '~/Dotfiles'
+HOMEDIR = Path.home()
+REMOVE_LEADING_DOT = True
+IGNORE_PATTERNS = ['.git', '.gitignore', 'README*', '*~']
class Repositories(object):
@@ -17,13 +17,13 @@ class Repositories(object):
def __init__(self, paths, dot):
if not paths:
- paths = [DEFAULT_PATH]
+ paths = [PATH]
if dot is None:
- dot = DEFAULT_REMOVE_LEADING_DOT
+ dot = REMOVE_LEADING_DOT
self.repos = []
for path in paths:
- path = py.path.local(path, expanduser=True)
+ path = Path(path).expanduser()
self.repos.append(Repository(path, dot))
def __len__(self):
@@ -37,53 +37,55 @@ class Repository(object):
"""A repository is a directory that contains dotfiles.
:param path: the location of the repository directory
+ :param homedir: the location of the home directory
:param remove_leading_dot: whether to remove the target's leading dot
:param ignore_patterns: a list of glob patterns to ignore
- :param homedir: the location of the home directory
"""
def __init__(self, path,
- remove_leading_dot=DEFAULT_REMOVE_LEADING_DOT,
- ignore_patterns=DEFAULT_IGNORE_PATTERNS,
- homedir=DEFAULT_HOMEDIR):
-
- # create repository directory if not found
- self.path = py.path.local(path).ensure_dir()
+ homedir=HOMEDIR,
+ remove_leading_dot=REMOVE_LEADING_DOT,
+ ignore_patterns=IGNORE_PATTERNS):
+ self.path = Path(path)
+ self.homedir = Path(homedir)
self.remove_leading_dot = remove_leading_dot
self.ignore_patterns = ignore_patterns
- self.homedir = homedir
+
+ # create repository directory if missing
+ self.path.mkdir(parents=True, exist_ok=True)
def __str__(self):
"""Return human-readable repository contents."""
-
- return ''.join('%s\n' % item for item in self.contents()).rstrip()
+ return ''.join('%s\n' % x for x in self.contents()).rstrip()
def __repr__(self):
- return '<Repository %r>' % self.path
+ 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.fnmatch(pattern):
+ if path.match(pattern):
return True
return False
def _dotfile_path(self, target):
"""Return the expected symlink for the given repository target."""
- relpath = self.path.bestrelpath(target)
+ relpath = target.relative_to(self.path)
if self.remove_leading_dot:
- return self.homedir.join('.%s' % relpath)
+ return self.homedir / ('.%s' % relpath)
else:
- return self.homedir.join(relpath)
+ return self.homedir / relpath
def _dotfile_target(self, path):
"""Return the expected repository target for the given symlink."""
- relpath = self.homedir.bestrelpath(path)
+ relpath = str(path.relative_to(self.homedir))
if self.remove_leading_dot:
- return self.path.join(relpath[1:])
+ return self.path / relpath[1:]
else:
- return self.path.join(relpath)
+ return self.path / relpath
def _dotfile(self, path):
"""Return a valid dotfile for the given path."""
@@ -103,11 +105,7 @@ class Repository(object):
def _contents(self, dir):
"""Return all unignored files contained below a directory."""
-
- def filter(node):
- return node.check(dir=0) and not self._ignore(node)
-
- return dir.visit(filter, lambda x: not self._ignore(x))
+ return [x for x in dir.rglob('*') if not self._ignore(x)]
def contents(self):
"""Return a list of dotfiles for each file in the repository."""
@@ -128,7 +126,7 @@ class Repository(object):
caller.
"""
- paths = list(set(map(py.path.local, paths)))
+ paths = list(set(map(Path, paths)))
for path in paths:
if path.check(dir=1):
@@ -139,7 +137,7 @@ class Repository(object):
try:
return self._dotfile(path)
except DotfileException as err:
- click.echo(err)
+ echo(err)
return None
return [d for d in map(construct, paths) if d is not None]
diff --git a/setup.py b/setup.py
index d12fd4c..90fd1ea 100644
--- a/setup.py
+++ b/setup.py
@@ -8,11 +8,6 @@ here = path.abspath(path.dirname(__file__))
with open(path.join(here, 'README.md'), encoding='utf8') as f:
long_description = f.read()
-requirements = [
- 'click',
- 'py',
-]
-
test_requirements = [
'pytest',
'pytest-pep8',
@@ -37,11 +32,9 @@ setup(
'Programming Language :: Python :: 3.6',
],
packages=find_packages(),
- install_requires=requirements,
- extras_require={
- 'dev': ['check-manifest'],
- 'test': test_requirements,
- },
+ install_requires=['click'],
+ setup_requires=['pytest-runner'],
+ tests_require=test_requirements,
entry_points={
'console_scripts': [
'dotfiles=dotfiles.cli:cli',
@@ -51,8 +44,4 @@ setup(
'Bug Reports': 'https://github.com/jbernard/dotfiles/issues',
'Source': 'https://github.com/jbernard/dotfiles',
},
-
- # temporary
- setup_requires=['pytest-runner'],
- tests_require=test_requirements,
)
diff --git a/tests/test_repository.py b/tests/test_repository.py
index 60ae8e3..ef78001 100644
--- a/tests/test_repository.py
+++ b/tests/test_repository.py
@@ -1,116 +1,133 @@
import pytest
-import py.path
+from pathlib import Path
from dotfiles.repository import Repository, \
- DEFAULT_REMOVE_LEADING_DOT, DEFAULT_IGNORE_PATTERNS
-from dotfiles.exceptions import NotRootedInHome, InRepository, TargetIgnored, \
- IsDirectory
+ REMOVE_LEADING_DOT, IGNORE_PATTERNS
def test_repo_create(repo):
- repo.path.remove()
- assert repo.path.check(exists=0)
+ repo.path.rmdir()
+ assert not repo.path.exists()
+
Repository(repo.path, repo.homedir)
- assert repo.path.check(exists=1, dir=1)
+ assert repo.path.exists()
+ assert repo.path.is_dir()
+
+@pytest.mark.parametrize('dot', [REMOVE_LEADING_DOT, not REMOVE_LEADING_DOT])
+@pytest.mark.parametrize('ignore', [IGNORE_PATTERNS, ['foo', 'bar', 'baz']])
+def test_params(repo, dot, ignore):
-@pytest.mark.parametrize('remove_leading_dot',
- [DEFAULT_REMOVE_LEADING_DOT,
- not DEFAULT_REMOVE_LEADING_DOT])
-@pytest.mark.parametrize('ignore_patterns', [DEFAULT_IGNORE_PATTERNS,
- ['foo', 'bar', 'baz']])
-def test_repo_params(repo, remove_leading_dot, ignore_patterns):
_repo = Repository(repo.path,
- remove_leading_dot=remove_leading_dot,
- ignore_patterns=ignore_patterns,
- homedir=repo.homedir)
+ homedir=repo.homedir,
+ remove_leading_dot=dot,
+ ignore_patterns=ignore)
+
assert _repo.path == repo.path
assert _repo.homedir == repo.homedir
- assert _repo.remove_leading_dot == remove_leading_dot
- assert _repo.ignore_patterns == ignore_patterns
+ assert _repo.remove_leading_dot == dot
+ assert _repo.ignore_patterns == ignore
+
+
+def test_contents(repo):
+ assert repo.contents() == []
+
+ target_a = repo.path / 'a'
+ target_b = repo.path / 'b/b'
+ target_c = repo.path / 'c/c/c'
+
+ target_b.parent.mkdir()
+ target_c.parent.mkdir(parents=True)
+
+ target_a.touch()
+ target_b.touch()
+ target_c.touch()
+
+ contents = repo.contents()
+
+ assert contents[0].target == target_a
+ assert contents[1].target == target_b
+ assert contents[2].target == target_c
def test_str(repo):
- repo.path.ensure('a')
- repo.path.ensure('b')
- repo.path.ensure('c')
+
+ Path(repo.path / 'a').touch()
+ Path(repo.path / 'b').touch()
+ Path(repo.path / 'c').touch()
+
assert str(repo) == (
- '%s\n%s\n%s' % (repo.homedir.join('.a'),
- repo.homedir.join('.b'),
- repo.homedir.join('.c')))
+ '%s\n%s\n%s' % (repo.homedir / '.a',
+ repo.homedir / '.b',
+ repo.homedir / '.c'))
@pytest.mark.parametrize('path', ['.foo', '.foo/bar/baz'])
def test_dotfile_path(repo, path):
+
repo.remove_leading_dot = False
- assert (repo._dotfile_path(repo.path.join(path)) ==
- repo.homedir.join(path))
+ assert (repo._dotfile_path(repo.path / path) ==
+ repo.homedir / path)
+
repo.remove_leading_dot = True
- assert (repo._dotfile_path(repo.path.join(path)) ==
- repo.homedir.join('.%s' % path))
+ assert (repo._dotfile_path(repo.path / path) ==
+ repo.homedir / ('.%s' % path))
@pytest.mark.parametrize('path', ['.foo', '.foo/bar/baz'])
def test_dotfile_target(repo, path):
- repo.remove_leading_dot = False
- assert (repo._dotfile_target(repo.homedir.join(path)) ==
- repo.path.join(path))
- repo.remove_leading_dot = True
- assert (repo._dotfile_target(repo.homedir.join(path)) ==
- repo.path.join(path[1:]))
+ repo.remove_leading_dot = False
+ assert (repo._dotfile_target(repo.homedir / path) ==
+ repo.path / path)
-def test_dotfile(repo):
- with pytest.raises(NotRootedInHome):
- repo._dotfile(py.path.local('/tmp/foo'))
- with pytest.raises(TargetIgnored):
- repo.ignore_patterns = ['.foo']
- repo.remove_leading_dot = False
- repo._dotfile(py.path.local(repo.homedir.join('.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'))
+ repo.remove_leading_dot = True
+ assert (repo._dotfile_target(repo.homedir / path) ==
+ repo.path / path[1:])
- # 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'))
+# from dotfiles.exceptions import NotRootedInHome, InRepository, TargetIgnored,
+# IsDirectory
-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')
+# def test_dotfile(repo):
+# with pytest.raises(NotRootedInHome):
+# repo._dotfile(py.path.local('/tmp/foo'))
+# with pytest.raises(TargetIgnored):
+# repo.ignore_patterns = ['.foo']
+# repo.remove_leading_dot = False
+# repo._dotfile(py.path.local(repo.homedir.join('.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'))
- dotfiles = repo.dotfiles([str(file), str(dir)])
- assert len(dotfiles) == 5
+# # 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_contents(repo):
- assert repo.contents() == []
- target_a = repo.path.ensure('a')
- target_b = repo.path.ensure('b/b')
- target_c = repo.path.ensure('c/c/c')
- contents = repo.contents()
+# 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')
- assert contents[0].target == target_a
- assert contents[1].target == target_b
- assert contents[2].target == target_c
+# 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')
+# 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
+# repo.prune()
+# assert len(repo.path.listdir()) == 0