Add uname command, Update uptime comand (#3909)

* Add uname support

* Changelog addition

* Migration to platform module

* Update uptime.py for the last version
Merge the original uptime module to a single file
Change the bootime() return be stay compatible with xonsh
Add support for Haiku , suppose to close #3882
Add dependency from uptime to support to MacOS 10.10 by add _posix.c file.

* Update uptime.py for the last version
Merge the original uptime module to a single file
Change the bootime() return be stay compatible with xonsh
Add support for Haiku , suppose to close #3882
Add dependency from uptime to support to MacOS 10.10 by add _posix.c file.

* typo fix

* black reformat

* remove usage of print

* black is black

* add original test for uptime
convert original test via 2to3
black reformat tests

* strange syntaxe fixe for flaske8

* black the incredible tool it stop a bug fixe just because it THE tool it want a return for make less readable code

* flake8 the famous tool it permit to stop a big fixe without any information's about the trouble

* workaround about xonsh CI don't respect docstring specs

* RISC OS only comment thing

* black is a good jock in a CI

* black is a good jock in a CI

* roll back uptime.py

* look if we can make it work

* fixe all i understand

* add command in corutils alias

* reformat uptime.py with black

* fixe version

* try with xonsh xp.LIBC lib

* black in a CI is a stupid thing

* stupid Windows and it \r

* use os.linestep

* use newline simple wrapper

* use newline simple wrapper

* use newline simple wrapper

* use newline simple wrapper

* try osx rollback method

* fixe

* fixe

* a test on window via the CI because i haven't the OS

* a test on window via the CI because i haven't the OS

* a test on window via the CI because i haven't the OS

* fix: black: format

* refactor: update uname command

now has auto-completions

* docs: update news item and fix qa error

* refactor: remove unused file

* fix: qa imports

* refactor: update getting boottime

fallback to monotonic time on unix

* fix: update haiku compatibility in uptime

* refactor: add uptime to aliases

* refactor: move xoreutils tests

* fix: call aliases using xonsh

Co-authored-by: Tuux <tuxa@rtnp.org>
Co-authored-by: Noortheen Raja <jnoortheen@gmail.com>
This commit is contained in:
Hierosme 2022-01-08 13:58:46 +01:00 committed by GitHub
parent 70dd8bf24b
commit 343ea33998
Failed to generate hash of commit
11 changed files with 365 additions and 217 deletions

23
news/fixe_uptime.rst Normal file
View file

@ -0,0 +1,23 @@
**Added:**
* added ``xonsh-uname`` command to ``xoreutils``
**Changed:**
* Update uptime lib by the last one from Pypi
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* <news item>
**Security:**
* <news item>

View file

@ -355,7 +355,9 @@ def main():
"pytest11": ["xonsh = xonsh.pytest_plugin"],
"console_scripts": [
"xonsh = xonsh.main:main",
"xonsh-cat = xonsh.xoreutils.cat:cat_main",
"xonsh-cat = xonsh.xoreutils.cat:main",
"xonsh-uname = xonsh.xoreutils.uname:main",
"xonsh-uptime = xonsh.xoreutils.uptime:main",
],
}
skw["cmdclass"]["develop"] = xdevelop

View file

@ -1,107 +1,10 @@
import io
import os
import tempfile
import pytest
from xonsh.xoreutils import _which, uptime, cat
from xonsh.tools import ON_WINDOWS
from xonsh.platform import DEFAULT_ENCODING
class TestWhich:
# Tests for the _whichgen function which is the only thing we
# use from the _which.py module.
def setup(self):
# Setup two folders with some test files.
self.testdirs = [tempfile.TemporaryDirectory(), tempfile.TemporaryDirectory()]
if ON_WINDOWS:
self.testapps = ["whichtestapp1.exe", "whichtestapp2.wta"]
self.exts = [".EXE"]
else:
self.testapps = ["whichtestapp1"]
self.exts = None
for app in self.testapps:
for d in self.testdirs:
path = os.path.join(d.name, app)
with open(path, "wb") as f:
f.write(b"")
os.chmod(path, 0o755)
def teardown_module(self):
for d in self.testdirs:
d.cleanup()
def test_whichgen(self):
testdir = self.testdirs[0].name
arg = "whichtestapp1"
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
assert len(matches) == 1
assert self._file_match(matches[0][0], os.path.join(testdir, arg))
def test_whichgen_failure(self):
testdir = self.testdirs[0].name
arg = "not_a_file"
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
assert len(matches) == 0
def test_whichgen_verbose(self):
testdir = self.testdirs[0].name
arg = "whichtestapp1"
matches = list(
_which.whichgen(arg, path=[testdir], exts=self.exts, verbose=True)
)
assert len(matches) == 1
match, from_where = matches[0]
assert self._file_match(match, os.path.join(testdir, arg))
assert from_where == "from given path element 0"
def test_whichgen_multiple(self):
testdir0 = self.testdirs[0].name
testdir1 = self.testdirs[1].name
arg = "whichtestapp1"
matches = list(_which.whichgen(arg, path=[testdir0, testdir1], exts=self.exts))
assert len(matches) == 2
assert self._file_match(matches[0][0], os.path.join(testdir0, arg))
assert self._file_match(matches[1][0], os.path.join(testdir1, arg))
if ON_WINDOWS:
def test_whichgen_ext_failure(self):
testdir = self.testdirs[0].name
arg = "whichtestapp2"
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
assert len(matches) == 0
def test_whichgen_ext_success(self):
testdir = self.testdirs[0].name
arg = "whichtestapp2"
matches = list(_which.whichgen(arg, path=[testdir], exts=[".wta"]))
assert len(matches) == 1
assert self._file_match(matches[0][0], os.path.join(testdir, arg))
def _file_match(self, path1, path2):
if ON_WINDOWS:
path1 = os.path.normpath(os.path.normcase(path1))
path2 = os.path.normpath(os.path.normcase(path2))
path1 = os.path.splitext(path1)[0]
path2 = os.path.splitext(path2)[0]
return path1 == path2
else:
return os.path.samefile(path1, path2)
def test_uptime():
up = uptime.uptime()
assert up is not None
assert up > 0.0
def test_boottime():
bt = uptime.boottime()
assert bt is not None
assert bt > 0.0
assert uptime._BOOTTIME is not None
assert uptime._BOOTTIME > 0.0
from xonsh.xoreutils import cat
@pytest.fixture

View file

@ -0,0 +1,15 @@
import platform
import pytest
@pytest.fixture
def uname(xession, load_xontrib):
load_xontrib("coreutils")
return xession.aliases["uname"]
def test_uname_without_args(uname):
out = uname(["-a"])
assert out.startswith(platform.uname().system)

View file

@ -0,0 +1,21 @@
#!/usr/bin/env python
import datetime
import sys
sys.path.insert(0, ".")
import pytest
@pytest.fixture
def uptime(xession, load_xontrib):
load_xontrib("coreutils")
return xession.aliases["uptime"]
def test_uptime(uptime):
out = uptime([])
delta = datetime.timedelta(seconds=float(out))
# make sure that it returns a positive time lapse
assert delta > datetime.timedelta(microseconds=1)

View file

@ -0,0 +1,87 @@
import os
import tempfile
from xonsh.tools import ON_WINDOWS
from xonsh.xoreutils import _which
class TestWhich:
# Tests for the _whichgen function which is the only thing we
# use from the _which.py module.
def setup(self):
# Setup two folders with some test files.
self.testdirs = [tempfile.TemporaryDirectory(), tempfile.TemporaryDirectory()]
if ON_WINDOWS:
self.testapps = ["whichtestapp1.exe", "whichtestapp2.wta"]
self.exts = [".EXE"]
else:
self.testapps = ["whichtestapp1"]
self.exts = None
for app in self.testapps:
for d in self.testdirs:
path = os.path.join(d.name, app)
with open(path, "wb") as f:
f.write(b"")
os.chmod(path, 0o755)
def teardown_module(self):
for d in self.testdirs:
d.cleanup()
def test_whichgen(self):
testdir = self.testdirs[0].name
arg = "whichtestapp1"
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
assert len(matches) == 1
assert self._file_match(matches[0][0], os.path.join(testdir, arg))
def test_whichgen_failure(self):
testdir = self.testdirs[0].name
arg = "not_a_file"
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
assert len(matches) == 0
def test_whichgen_verbose(self):
testdir = self.testdirs[0].name
arg = "whichtestapp1"
matches = list(
_which.whichgen(arg, path=[testdir], exts=self.exts, verbose=True)
)
assert len(matches) == 1
match, from_where = matches[0]
assert self._file_match(match, os.path.join(testdir, arg))
assert from_where == "from given path element 0"
def test_whichgen_multiple(self):
testdir0 = self.testdirs[0].name
testdir1 = self.testdirs[1].name
arg = "whichtestapp1"
matches = list(_which.whichgen(arg, path=[testdir0, testdir1], exts=self.exts))
assert len(matches) == 2
assert self._file_match(matches[0][0], os.path.join(testdir0, arg))
assert self._file_match(matches[1][0], os.path.join(testdir1, arg))
if ON_WINDOWS:
def test_whichgen_ext_failure(self):
testdir = self.testdirs[0].name
arg = "whichtestapp2"
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
assert len(matches) == 0
def test_whichgen_ext_success(self):
testdir = self.testdirs[0].name
arg = "whichtestapp2"
matches = list(_which.whichgen(arg, path=[testdir], exts=[".wta"]))
assert len(matches) == 1
assert self._file_match(matches[0][0], os.path.join(testdir, arg))
def _file_match(self, path1, path2):
if ON_WINDOWS:
path1 = os.path.normpath(os.path.normcase(path1))
path2 = os.path.normpath(os.path.normcase(path2))
path1 = os.path.splitext(path1)[0]
path2 = os.path.splitext(path2)[0]
return path1 == path2
else:
return os.path.samefile(path1, path2)

View file

@ -5,7 +5,7 @@ import time
import xonsh.procs.pipelines as xpp
from xonsh.built_ins import XSH
from xonsh.xoreutils.util import arg_handler
from xonsh.xoreutils.util import arg_handler, run_alias
def _cat_line(
@ -156,14 +156,9 @@ Examples:
# --version output version information and exit"""
def cat_main(args=None):
import sys
from xonsh.main import setup
setup()
args = sys.argv[1:] if args is None else args
cat(args, sys.stdin, sys.stdout, sys.stderr)
def main(args=None):
run_alias("cat", args)
if __name__ == "__main__":
cat_main()
main()

96
xonsh/xoreutils/uname.py Normal file
View file

@ -0,0 +1,96 @@
#!/usr/bin/env python
"""
Provides a cross-platform way to figure out the system uname.
This version of uname was written in Python for the xonsh project: http://xon.sh
Based on cat from GNU coreutils: http://www.gnu.org/software/coreutils/
"""
import platform
import sys
from xonsh.cli_utils import ArgParserAlias
def uname_fn(
all=False,
kernel_name=False,
node_name=False,
kernel_release=False,
kernel_version=False,
machine=False,
processor=False,
hardware_platform=False,
operating_system=False,
):
"""This version of uname was written in Python for the xonsh project: https://xon.sh
Based on uname from GNU coreutils: http://www.gnu.org/software/coreutils/
Parameters
----------
all : -a, --all
print all information, in the following order, except omit -p and -i if unknown
kernel_name : -s, --kernel-name
print the kernel name
node_name : -n, --nodename
print the network node hostname
kernel_release : -r, --kernel-release
print the kernel release
kernel_version : -v, --kernel-version
print the kernel version
machine : -m, --machine
print the machine hardware name
processor : -p, --processor
print the processor type (non-portable)
hardware_platform : -i, --hardware-platform
print the hardware platform (non-portable)
operating_system : -o, --operating-system
print the operating system
"""
info = platform.uname()
def gen_lines():
if all or node_name:
yield info.node
if all or kernel_release:
yield info.release
if all or kernel_version:
yield info.version
if all or machine:
yield info.machine
if all or processor:
yield info.processor or "unknown"
if all or hardware_platform:
yield "unknown"
if all or operating_system:
yield sys.platform
lines = list(gen_lines())
if all or kernel_name or (not lines):
lines.insert(0, info.system)
line = " ".join(lines)
return line
uname = ArgParserAlias(func=uname_fn, has_args=True, prog="uname")
def main(args=None):
from xonsh.xoreutils.util import run_alias
run_alias("uname", args)
if __name__ == "__main__":
main()

View file

@ -10,23 +10,20 @@ to standard output.
This file was forked from the uptime project: https://github.com/Cairnarvon/uptime
Copyright (c) 2012, Koen Crolla, All rights reserved.
"""
import contextlib
import ctypes
import functools
import os
import struct
import sys
import time
import ctypes
import struct
import typing as tp
import xonsh.platform as xp
import xonsh.lazyimps as xlimps
import xonsh.lazyasd as xl
_BOOTTIME: tp.Optional[float] = None
import xonsh.platform as xp
def _uptime_osx():
def _boot_time_osx() -> "float|None":
"""Returns the uptime on mac / darwin."""
global _BOOTTIME
bt = xlimps.macutils.sysctlbyname(b"kern.boottime", return_str=False)
if len(bt) == 4:
bt = struct.unpack_from("@hh", bt)
@ -39,62 +36,50 @@ def _uptime_osx():
bt = bt[0] + bt[1] * 1e-6
if bt == 0.0:
return None
_BOOTTIME = bt
return time.time() - bt
return bt
def _uptime_linux():
"""Returns uptime in seconds or None, on Linux."""
# With procfs
try:
with open("/proc/uptime") as f:
up = float(f.readline().split()[0])
return up
except (OSError, ValueError):
pass
buf = ctypes.create_string_buffer(128) # 64 suffices on 32-bit, whatever.
if xp.LIBC.sysinfo(buf) < 0:
return None
up = struct.unpack_from("@l", buf.raw)[0]
if up < 0:
up = None
return up
def _boottime_linux():
def _boot_time_linux() -> "float|None":
"""A way to figure out the boot time directly on Linux."""
global _BOOTTIME
# from the answer here -
# https://stackoverflow.com/questions/42471475/fastest-way-to-get-system-uptime-in-python-in-linux
bt_flag = getattr(time, "CLOCK_BOOTTIME", None)
if bt_flag is not None:
return time.clock_gettime(bt_flag)
try:
with open("/proc/stat") as f:
for line in f:
if line.startswith("btime"):
_BOOTTIME = float(line.split()[1])
return _BOOTTIME
return float(line.split()[1])
except (OSError, IndexError):
return None
def _uptime_amiga():
def _boot_time_amiga() -> "float|None":
"""Returns uptime in seconds or None, on AmigaOS."""
global _BOOTTIME
try:
_BOOTTIME = os.stat("RAM:").st_ctime
return time.time() - _BOOTTIME
return os.stat("RAM:").st_ctime
except (NameError, OSError):
return None
def _uptime_beos():
def _boot_time_beos() -> "float|None":
"""Returns uptime in seconds on None, on BeOS/Haiku."""
if not hasattr(xp.LIBC, "system_time"):
return None
xp.LIBC.system_time.restype = ctypes.c_int64
return xp.LIBC.system_time() / 1000000.0
return time.time() - (xp.LIBC.system_time() / 1000000.0)
def _uptime_bsd():
def _boot_time_bsd() -> "float|None":
"""Returns uptime in seconds or None, on BSD (including OS X)."""
global _BOOTTIME
# https://docs.python.org/3/library/time.html#time.CLOCK_UPTIME
with contextlib.suppress(Exception):
ut_flag = getattr(time, "CLOCK_UPTIME", None)
if ut_flag is not None:
ut = time.clock_gettime(ut_flag)
return time.time() - ut
if not hasattr(xp.LIBC, "sysctlbyname"):
# Not BSD.
return None
@ -111,24 +96,20 @@ def _uptime_bsd():
# OS X disagrees what that second value is.
if usec > 1000000:
usec = 0.0
_BOOTTIME = sec + usec / 1000000.0
up = time.time() - _BOOTTIME
if up < 0:
up = None
return up
return sec + usec / 1000000.0
def _uptime_minix():
def _boot_time_minix():
"""Returns uptime in seconds or None, on MINIX."""
try:
with open("/proc/uptime") as f:
up = float(f.read())
return up
return time.time() - up
except (OSError, ValueError):
return None
def _uptime_plan9():
def _boot_time_plan9():
"""Returns uptime in seconds or None, on Plan 9."""
# Apparently Plan 9 only has Python 2.2, which I'm not prepared to
# support. Maybe some Linuxes implement /dev/time, though, someone was
@ -141,19 +122,19 @@ def _uptime_plan9():
# -- cons(3)
with open("/dev/time") as f:
s, ns, ct, cf = f.read().split()
return float(ct) / float(cf)
return time.time() - (float(ct) / float(cf))
except (OSError, ValueError):
return None
def _uptime_solaris():
def _boot_time_solaris():
"""Returns uptime in seconds or None, on Solaris."""
global _BOOTTIME
try:
kstat = ctypes.CDLL("libkstat.so")
except (AttributeError, OSError):
return None
_BOOTTIME = None
# kstat doesn't have uptime, but it does have boot time.
# Unfortunately, getting at it isn't perfectly straightforward.
# First, let's pretend to be kstat.h
@ -202,83 +183,93 @@ def _uptime_solaris():
_BOOTTIME = data.contents.value.time
# Clean-up.
kstat.kstat_close(kc)
if _BOOTTIME is not None:
return time.time() - _BOOTTIME
return None
return _BOOTTIME
def _uptime_syllable():
def _boot_time_syllable():
"""Returns uptime in seconds or None, on Syllable."""
global _BOOTTIME
try:
_BOOTTIME = os.stat("/dev/pty/mst/pty0").st_mtime
return time.time() - _BOOTTIME
return os.stat("/dev/pty/mst/pty0").st_mtime
except (NameError, OSError):
return None
def _uptime_windows():
def _boot_time_windows():
"""
Returns uptime in seconds or None, on Windows. Warning: may return
incorrect answers after 49.7 days on versions older than Vista.
"""
uptime = None
if hasattr(xp.LIBC, "GetTickCount64"):
# Vista/Server 2008 or later.
xp.LIBC.GetTickCount64.restype = ctypes.c_uint64
return xp.LIBC.GetTickCount64() / 1000.0
uptime = xp.LIBC.GetTickCount64() / 1000.0
if hasattr(xp.LIBC, "GetTickCount"):
# WinCE and Win2k or later; gives wrong answers after 49.7 days.
xp.LIBC.GetTickCount.restype = ctypes.c_uint32
return xp.LIBC.GetTickCount() / 1000.0
uptime = xp.LIBC.GetTickCount() / 1000.0
if uptime:
return time.time() - uptime
return None
@xl.lazyobject
def _UPTIME_FUNCS():
return {
"amiga": _uptime_amiga,
"aros12": _uptime_amiga,
"beos5": _uptime_beos,
"cygwin": _uptime_linux,
"darwin": _uptime_osx,
"haiku1": _uptime_beos,
"linux": _uptime_linux,
"linux-armv71": _uptime_linux,
"linux2": _uptime_linux,
"minix3": _uptime_minix,
"sunos5": _uptime_solaris,
"syllable": _uptime_syllable,
"win32": _uptime_windows,
"wince": _uptime_windows,
}
def _boot_time_monotonic():
# https://stackoverflow.com/a/68197354/5048394
if hasattr(time, "CLOCK_MONOTONIC"):
# this will work on unix systems
monotime = time.clock_gettime(time.CLOCK_MONOTONIC)
else:
monotime = time.time()
return time.time() - monotime
def uptime():
def _get_boot_time_func():
plat = sys.platform
if plat.startswith(("amiga", "aros12")):
return _boot_time_amiga
if plat.startswith(("beos5", "haiku1")):
return _boot_time_beos()
if plat.startswith(("cygwin", "linux")):
# "cygwin", "linux","linux-armv71": "linux2"
return _boot_time_linux
if plat.startswith("minix3"):
return _boot_time_minix
if plat.startswith("darwin"):
return _boot_time_osx
if plat.startswith("sunos5"):
return _boot_time_solaris
if plat.startswith("syllable"):
return _boot_time_syllable
# tried with all unix stuff
if plat.startswith("win"):
return _boot_time_windows
# fallback
return _boot_time_monotonic
def uptime(args):
"""Returns uptime in seconds if even remotely possible, or None if not."""
if _BOOTTIME is not None:
return time.time() - _BOOTTIME
up = _UPTIME_FUNCS.get(sys.platform, _uptime_bsd)()
if up is None:
up = (
_uptime_bsd()
or _uptime_plan9()
or _uptime_linux()
or _uptime_windows()
or _uptime_solaris()
or _uptime_beos()
or _uptime_amiga()
or _uptime_syllable()
or _uptime_osx()
)
return up
bt = boottime()
return str(time.time() - bt)
def boottime():
@functools.lru_cache(None)
def boottime() -> "float":
"""Returns boot time if remotely possible, or None if not."""
global _BOOTTIME
if _BOOTTIME is None:
up = uptime()
if up is None:
return None
_BOOTTIME = time.time() - up
return _BOOTTIME
func = _get_boot_time_func()
btime = func()
if btime is None:
return _boot_time_monotonic()
return btime
def main(args=None):
from xonsh.xoreutils.util import run_alias
run_alias("uptime", args)
if __name__ == "__main__":
main()

View file

@ -17,3 +17,17 @@ def arg_handler(args, out, short, key, val, long=None):
out[k] = val
else:
out[key] = val
def run_alias(name: str, args=None):
import sys
from xonsh.main import setup
from xonsh.built_ins import subproc_uncaptured
from xonsh.xontribs import xontribs_load
setup()
xontribs_load(["coreutils"])
args = sys.argv[1:] if args is None else args
subproc_uncaptured([name] + args)

View file

@ -14,14 +14,13 @@ tools avoid the need for a full subprocess call. Additionally, these
tools are cross-platform.
"""
from xonsh.built_ins import XSH
from xonsh.platform import (
ON_POSIX,
)
from xonsh.platform import ON_POSIX
from xonsh.xoreutils.cat import cat
from xonsh.xoreutils.echo import echo
from xonsh.xoreutils.pwd import pwd
from xonsh.xoreutils.tee import tee
from xonsh.xoreutils.tty import tty
from xonsh.xoreutils.uname import uname
from xonsh.xoreutils.umask import umask
from xonsh.xoreutils.uptime import uptime
from xonsh.xoreutils.yes import yes
@ -33,6 +32,8 @@ XSH.aliases["echo"] = echo
XSH.aliases["pwd"] = pwd
XSH.aliases["tee"] = tee
XSH.aliases["tty"] = tty
XSH.aliases["uname"] = uname
XSH.aliases["uptime"] = uptime
XSH.aliases["yes"] = yes
XSH.aliases["umask"] = umask
XSH.aliases["uptime"] = uptime