Skip to content

Commit

Permalink
Merge pull request #726 from amoffat/develop
Browse files Browse the repository at this point in the history
Release 2.0.7
  • Loading branch information
amoffat committed Jun 1, 2024
2 parents 08d7603 + a451ef8 commit 49be03a
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 35 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ jobs:
needs: test
runs-on: ubuntu-latest
if: github.ref_name == 'master'

permissions:
id-token: write

steps:
- uses: actions/checkout@v2

Expand All @@ -163,5 +167,3 @@ jobs:

- name: Publish
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 2.0.7 - 5/31/24

- Fix `sh.glob` arguments [#708](https://github.com/amoffat/sh/issues/708)
- Misc modernizations

## 2.0.6 - 8/9/23

- Add back appropriate sdist files [comment](https://github.com/amoffat/sh/commit/89333ae48069a5b445b3535232195b2de6f4648f)
Expand Down
4 changes: 2 additions & 2 deletions docs/source/sections/exit_codes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
Exit Codes & Exceptions
=======================

Normal processes exit with exit code 0. This can be seen through a
Normal processes exit with exit code 0. This can be seen from
:attr:`RunningCommand.exit_code`:

.. code-block:: python
output = ls("/")
output = ls("/", _return_cmd=True)
print(output.exit_code) # should be 0
If a process terminates, and the exit code is not 0, an exception is generated
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "sh"
version = "2.0.6"
version = "2.0.7"
description = "Python subprocess replacement"
authors = ["Andrew Moffat <[email protected]>"]
readme = "README.rst"
Expand Down
48 changes: 24 additions & 24 deletions sh.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
try:
from collections.abc import Mapping
except ImportError: # pragma: no cover
from collections import Mapping
from collections.abc import Mapping

import errno
import fcntl
Expand Down Expand Up @@ -106,7 +106,7 @@ def get_num_args(fn):
return len(inspect.getfullargspec(fn).args)


_unicode_methods = set(dir(str()))
_unicode_methods = set(dir(""))

HAS_POLL = hasattr(select, "poll")
POLLER_EVENT_READ = 1
Expand All @@ -115,7 +115,7 @@ def get_num_args(fn):
POLLER_EVENT_ERROR = 8


class PollPoller(object):
class PollPoller:
def __init__(self):
self._poll = select.poll()
# file descriptor <-> file object bidirectional maps
Expand Down Expand Up @@ -191,7 +191,7 @@ def poll(self, timeout):
return results


class SelectPoller(object):
class SelectPoller:
def __init__(self):
self.rlist = []
self.wlist = []
Expand Down Expand Up @@ -271,7 +271,7 @@ class ErrorReturnCodeMeta(type):
"""

def __subclasscheck__(self, o):
other_bases = set([b.__name__ for b in o.__bases__])
other_bases = {b.__name__ for b in o.__bases__}
return self.__name__ in other_bases or o.__name__ == self.__name__


Expand Down Expand Up @@ -330,7 +330,7 @@ def __init__(self, full_cmd, stdout, stderr, truncate=True):
f"\n\n STDERR:\n{exc_stderr.decode(DEFAULT_ENCODING, 'replace')}"
)

super(ErrorReturnCode, self).__init__(msg)
super().__init__(msg)


class SignalException(ErrorReturnCode):
Expand Down Expand Up @@ -372,9 +372,9 @@ class CommandNotFound(AttributeError):
rc_exc_regex = re.compile(r"(ErrorReturnCode|SignalException)_((\d+)|SIG[a-zA-Z]+)")
rc_exc_cache: Dict[str, Type[ErrorReturnCode]] = {}

SIGNAL_MAPPING = dict(
[(v, k) for k, v in signal.__dict__.items() if re.match(r"SIG[a-zA-Z]+", k)]
)
SIGNAL_MAPPING = {
v: k for k, v in signal.__dict__.items() if re.match(r"SIG[a-zA-Z]+", k)
}


def get_exc_from_name(name):
Expand Down Expand Up @@ -457,12 +457,12 @@ def __init__(self, path, results):
list.__init__(self, results)


def glob(path, recursive=False):
expanded = GlobResults(path, _old_glob(path, recursive=recursive))
def glob(path, *args, **kwargs):
expanded = GlobResults(path, _old_glob(path, *args, **kwargs))
return expanded


glob_module.glob = glob
glob_module.glob = glob # type: ignore


def canonicalize(path):
Expand Down Expand Up @@ -536,7 +536,7 @@ def resolve_command(name, command_cls, baked_args=None):
return cmd


class Logger(object):
class Logger:
"""provides a memory-inexpensive logger. a gotcha about python's builtin
logger is that logger objects are never garbage collected. if you create a
thousand loggers with unique names, they'll sit there in memory until your
Expand Down Expand Up @@ -596,7 +596,7 @@ def default_logger_str(cmd, call_args, pid=None):
return s


class RunningCommand(object):
class RunningCommand:
"""this represents an executing Command object. it is returned as the
result of __call__() being executed on a Command instance. this creates a
reference to a OProc instance, which is a low-level wrapper around the
Expand Down Expand Up @@ -1158,7 +1158,7 @@ def env_validator(passed_kwargs, merged_kwargs):
return invalid


class Command(object):
class Command:
"""represents an un-run system program, like "ls" or "cd". because it
represents the program itself (and not a running instance of it), it should
hold very little state. in fact, the only state it does hold is baked
Expand Down Expand Up @@ -1773,7 +1773,7 @@ def no_interrupt(syscall, *args, **kwargs):
return ret


class OProc(object):
class OProc:
"""this class is instantiated by RunningCommand for a command to be exec'd.
it handles all the nasty business involved with correctly setting up the
input/output to the child process. it gets its name for subprocess.Popen
Expand Down Expand Up @@ -2088,12 +2088,12 @@ def __init__(
# don't inherit file descriptors
try:
inherited_fds = os.listdir("/dev/fd")
except (IOError, OSError):
except OSError:
# Some systems don't have /dev/fd. Raises OSError in
# Python2, FileNotFoundError on Python3. The latter doesn't
# exist on Python2, but inherits from IOError, which does.
inherited_fds = os.listdir("/proc/self/fd")
inherited_fds = set(int(fd) for fd in inherited_fds) - pass_fds
inherited_fds = {int(fd) for fd in inherited_fds} - pass_fds
for fd in inherited_fds:
try:
os.close(fd)
Expand Down Expand Up @@ -2870,7 +2870,7 @@ def bufsize_type_to_bufsize(bf_type):
return bufsize


class StreamWriter(object):
class StreamWriter:
"""StreamWriter reads from some input (the stdin param) and writes to a fd
(the stream param). the stdin may be a Queue, a callable, something with
the "read" method, a string, or an iterable"""
Expand Down Expand Up @@ -3073,7 +3073,7 @@ def finish():
return process, finish


class StreamReader(object):
class StreamReader:
"""reads from some output (the stream) and sends what it just read to the
handler."""

Expand Down Expand Up @@ -3160,7 +3160,7 @@ def read(self):
self.write_chunk(chunk)


class StreamBufferer(object):
class StreamBufferer:
"""this is used for feeding in chunks of stdout/stderr, and breaking it up
into chunks that will actually be put into the internal buffers. for
example, if you have two processes, one being piped to the other, and you
Expand Down Expand Up @@ -3506,7 +3506,7 @@ def process(a, kwargs):
def ssh(orig): # pragma: no cover
"""An ssh command for automatic password login"""

class SessionContent(object):
class SessionContent:
def __init__(self):
self.chars = deque(maxlen=50000)
self.lines = deque(maxlen=5000)
Expand All @@ -3531,7 +3531,7 @@ def cur_line(self):
line = "".join(self.line_chars)
return line

class SSHInteract(object):
class SSHInteract:
def __init__(self, prompt_match, pass_getter, out_handler, login_success):
self.prompt_match = prompt_match
self.pass_getter = pass_getter
Expand Down Expand Up @@ -3626,7 +3626,7 @@ def __init__(self, self_module, baked_args=None):
# but it seems to be the only way to make reload() behave
# nicely. if i make these attributes dynamic lookups in
# __getattr__, reload sometimes chokes in weird ways...
super(SelfWrapper, self).__init__(
super().__init__(
name=getattr(self_module, "__name__", None),
doc=getattr(self_module, "__doc__", None),
)
Expand Down
11 changes: 5 additions & 6 deletions tests/sh_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf8 -*-
import asyncio
import errno
import fcntl
Expand Down Expand Up @@ -446,7 +445,7 @@ def test_multiple_pipes(self):
def inc(*args, **kwargs):
return python("-u", inc_py.name, *args, **kwargs)

class Derp(object):
class Derp:
def __init__(self):
self.times = []
self.stdout = []
Expand Down Expand Up @@ -795,7 +794,7 @@ def test_doesnt_execute_directories(self):
self.assertEqual(gcc._path, gcc_file2)
self.assertEqual(
gcc("no-error", _return_cmd=True).stdout.strip(),
"no-error".encode("ascii"),
b"no-error",
)

finally:
Expand Down Expand Up @@ -2308,7 +2307,7 @@ def test_callable_interact(self):
"""
)

class Callable(object):
class Callable:
def __init__(self):
self.line = None

Expand Down Expand Up @@ -2647,7 +2646,7 @@ def test_baked_command_can_be_printed(self):
def test_done_callback(self):
import time

class Callback(object):
class Callback:
def __init__(self):
self.called = False
self.exit_code = None
Expand Down Expand Up @@ -2792,7 +2791,7 @@ def session_false_group_true(pid, pgid, sid, test_pid):
def test_done_cb_exc(self):
from sh import ErrorReturnCode

class Callback(object):
class Callback:
def __init__(self):
self.called = False
self.success = None
Expand Down

0 comments on commit 49be03a

Please sign in to comment.