Skip to content

Commit

Permalink
Merge pull request #244 from henryiii/pytest
Browse files Browse the repository at this point in the history
Py.test conversion
  • Loading branch information
henryiii committed Nov 18, 2015
2 parents b4daef6 + ae6f2ea commit b51c4d9
Show file tree
Hide file tree
Showing 20 changed files with 750 additions and 711 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* Bugfix: ``Progress`` works on Python 2.6 (`#230 <https://github.com/tomerfiliba/plumbum/issues/230>`_),
* Bugfix: Colors now work with more terminals (`#231 <https://github.com/tomerfiliba/plumbum/issues/231>`_)
* Bugfix: Getting an executible no longer returns a directory (`#234 <https://ithub.com/tomerfiliba/plumbum/issues/234>`_)
* Bugfix: Iterdir now works on Python <3.5
* Testing is now expanded and fully written in Pytest, with coverage reporting.

1.6.0
-----
Expand Down
37 changes: 7 additions & 30 deletions plumbum/_testtools.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,12 @@
import pytest
import os
import sys
import unittest
from plumbum import local
from plumbum.lib import IS_WIN32

def ensure_skipIf(unittest):
"""
This will ensure that unittest has skipIf. Call like::
skip_without_chown = pytest.mark.skipif(not hasattr(os, "chown"),
reason="os.chown not supported")

import unittest
ensure_skipIf(unittest)
"""
skip_without_tty = pytest.mark.skipif(not sys.stdin.isatty(),
reason="Not a TTY")

if not hasattr(unittest, "skipIf"):
import logging
import functools
def skipIf(condition, reason):
def deco(func):
if not condition:
return func
else:
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.warn("skipping test: "+reason)
return wrapper
return deco
unittest.skipIf = skipIf

ensure_skipIf(unittest)
skipIf = unittest.skipIf

skip_on_windows = unittest.skipIf(IS_WIN32, "Does not work on Windows (yet)")
skip_without_chown = unittest.skipIf(not hasattr(os, "chown"), "os.chown not supported")
skip_without_tty = unittest.skipIf(not sys.stdin.isatty(), "Not a TTY")
skip_on_windows = pytest.mark.skipif(sys.platform == "win32",
reason="Windows not supported for this test (yet)")
4 changes: 2 additions & 2 deletions plumbum/cli/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def display(self):
sys.stdout.flush()


class ProgressIPy(ProgressBase): # pragma: nocover
class ProgressIPy(ProgressBase): # pragma: no cover
HTMLBOX = '<div class="widget-hbox widget-progress"><div class="widget-label" style="display:block;">{}</div></div>'

def __init__(self, *args, **kargs):
Expand Down Expand Up @@ -212,7 +212,7 @@ class ProgressAuto(ProgressBase):
"""
def __new__(cls, *args, **kargs):
"""Uses the generator trick that if a cls instance is returned, the __init__ method is not called."""
try: #pragma: nocover
try: # pragma: no cover
__IPYTHON__
try:
from traitlets import TraitError
Expand Down
4 changes: 2 additions & 2 deletions plumbum/cli/termsize.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def get_terminal_size(default=(80, 25)):
Originally from: http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python
"""
current_os = platform.system()
if current_os == 'Windows':
if current_os == 'Windows': # pragma: no branch
size = _get_terminal_size_windows()
if not size:
# needed for window's python in cygwin's xterm!
Expand All @@ -28,7 +28,7 @@ def get_terminal_size(default=(80, 25)):
size = default
return size

def _get_terminal_size_windows():
def _get_terminal_size_windows(): # pragma: no cover
try:
from ctypes import windll, create_string_buffer
STDERR_HANDLE = -12
Expand Down
4 changes: 2 additions & 2 deletions plumbum/commands/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,14 +276,14 @@ def machine(self):
def popen(self, args = (), **kwargs):
src_kwargs = kwargs.copy()
src_kwargs["stdout"] = PIPE
src_kwargs["stderr"] = PIPE

srcproc = self.srccmd.popen(args, **src_kwargs)
kwargs["stdin"] = srcproc.stdout
dstproc = self.dstcmd.popen(**kwargs)
# allow p1 to receive a SIGPIPE if p2 exits
srcproc.stdout.close()
srcproc.stderr.close()
if srcproc.stderr is not None:
dstproc.stderr = srcproc.stderr
if srcproc.stdin:
srcproc.stdin.close()
dstproc.srcproc = srcproc
Expand Down
2 changes: 2 additions & 0 deletions plumbum/machines/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ def _path_mkdir(self, fn):
self._session.run("mkdir -p %s" % (shquote(fn),))
def _path_chmod(self, mode, fn):
self._session.run("chmod %o %s" % (mode, shquote(fn)))
def _path_touch(self, path):
self._session.run("touch {path}".format(path=path))
def _path_chown(self, fn, owner, group, recursive):
args = ["chown"]
if recursive:
Expand Down
8 changes: 6 additions & 2 deletions plumbum/path/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ def rename(self, newname):
return self.move(self.up() / newname)
@abstractmethod
def copy(self, dst, override = False):
"""Copies this path (recursively, if a directory) to the destination path"""
"""Copies this path (recursively, if a directory) to the destination path. Raises TypeError if
dst exists and override is False."""
@abstractmethod
def mkdir(self):
"""Creates a directory at this path; if the directory already exists, silently ignore"""
Expand All @@ -229,6 +230,9 @@ def write(self, data, encoding=None):
"""writes the given data to this file. By default the data is expected to be binary (``bytes``),
but you can specify the encoding, e.g., ``'latin1'`` or ``'utf8'``"""
@abstractmethod
def touch(self):
"""Update the access time. Creates an empty file if none exists."""
@abstractmethod
def chown(self, owner = None, group = None, recursive = None):
"""Change ownership of this path.
Expand Down Expand Up @@ -292,7 +296,7 @@ def split(self):
@property
def parts(self):
"""Splits the directory into parts, including the base directroy, returns a tuple"""
return tuple([self.root] + self.split())
return tuple([self.drive + self.root] + self.split())

def relative_to(self, source):
"""Computes the "relative path" require to get from ``source`` to ``self``. They satisfy the invariant
Expand Down
21 changes: 13 additions & 8 deletions plumbum/path/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __new__(cls, *parts):
not isinstance(parts[0], LocalWorkdir):
return parts[0]
if not parts:
raise TypeError("At least one path part is require (none given)")
raise TypeError("At least one path part is required (none given)")
if any(isinstance(path, RemotePath) for path in parts):
raise TypeError("LocalPath cannot be constructed from %r" % (parts,))
self = super(LocalPath, cls).__new__(cls, os.path.normpath(os.path.join(*(str(p) for p in parts))))
Expand All @@ -57,8 +57,6 @@ def _path(self):

def _get_info(self):
return self._path
def __getstate__(self):
return {"_path" : self._path}

def _form(self, *parts):
return LocalPath(*parts)
Expand Down Expand Up @@ -114,8 +112,8 @@ def list(self):
@_setdoc(Path)
def iterdir(self):
try:
return (self.__class__(fn.name) for fn in os.scandir(str(self)))
except NameError:
return (self / fn.name for fn in os.scandir(str(self)))
except AttributeError:
return (self / fn for fn in os.listdir(str(self)))

@_setdoc(Path)
Expand Down Expand Up @@ -171,7 +169,7 @@ def delete(self):
else:
try:
os.remove(str(self))
except OSError:
except OSError: # pragma: no cover
# file might already been removed (a race with other threads/processes)
_, ex, _ = sys.exc_info()
if ex.errno != errno.ENOENT:
Expand All @@ -191,6 +189,8 @@ def copy(self, dst, override = False):
dst = LocalPath(dst)
if override:
dst.delete()
elif dst.exists():
raise TypeError("File exists and override was not specified")
if self.is_dir():
shutil.copytree(str(self), str(dst))
else:
Expand All @@ -205,7 +205,7 @@ def mkdir(self):
if not self.exists():
try:
os.makedirs(str(self))
except OSError:
except OSError: # pragma: no cover
# directory might already exist (a race with other threads/processes)
_, ex, _ = sys.exc_info()
if ex.errno != errno.EEXIST:
Expand All @@ -230,6 +230,11 @@ def write(self, data, encoding=None):
with self.open("wb") as f:
f.write(data)

@_setdoc(Path)
def touch(self):
with open(str(self), 'a'):
os.utime(str(self), None)

@_setdoc(Path)
def chown(self, owner = None, group = None, recursive = None):
if not hasattr(os, "chown"):
Expand Down Expand Up @@ -287,7 +292,7 @@ def unlink(self):
else:
# windows: use rmdir for directories and directory symlinks
os.rmdir(str(self))
except OSError:
except OSError: # pragma: no cover
# file might already been removed (a race with other threads/processes)
_, ex, _ = sys.exc_info()
if ex.errno != errno.ENOENT:
Expand Down
10 changes: 10 additions & 0 deletions plumbum/path/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ def copy(self, dst, override = False):
if isinstance(dst, six.string_types):
dst = RemotePath(self.remote, dst)
dst.remove()
else:
if isinstance(dst, six.string_types):
dst = RemotePath(self.remote, dst)
if dst.exists():
raise TypeError("Override not specified and dst exists")

self.remote._path_copy(self, dst)

@_setdoc(Path)
Expand All @@ -229,6 +235,10 @@ def write(self, data, encoding=None):
data = data.encode(encoding)
self.remote._path_write(self, data)

@_setdoc(Path)
def touch(self):
self.remote._path_touch(str(self))

@_setdoc(Path)
def chown(self, owner = None, group = None, recursive = None):
self.remote._path_chown(self, owner, group, self.is_dir() if recursive is None else recursive)
Expand Down
2 changes: 1 addition & 1 deletion tests/pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
# This is for py.test -f with xdist plugin
looponfailroots = . ../plumbum

addopts = -v --capture=sys -rw
addopts = -v -rswx
Loading

0 comments on commit b51c4d9

Please sign in to comment.