added uptime

This commit is contained in:
Anthony Scopatz 2017-02-04 14:12:28 -05:00
parent e2bdc263d0
commit ac40750c04
8 changed files with 380 additions and 15 deletions

View file

@ -2,6 +2,7 @@ import os
import tempfile
from xonsh.xoreutils import _which
from xonsh.xoreutils import uptime
from xonsh.tools import ON_WINDOWS
@ -85,3 +86,17 @@ class TestWhich:
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

View file

@ -1,7 +1,7 @@
__version__ = '0.5.3'
# amalgamate exclude jupyter_kernel parser_table parser_test_table pyghooks
# amalgamate exclude winutils wizard pytest_plugin fs
# amalgamate exclude winutils wizard pytest_plugin fs macutils
import os as _os
if _os.getenv('XONSH_DEBUG', ''):
pass

View file

@ -11,7 +11,7 @@ import subprocess
import collections
from xonsh.lazyasd import LazyObject
from xonsh.platform import ON_DARWIN, ON_WINDOWS, ON_CYGWIN
from xonsh.platform import ON_DARWIN, ON_WINDOWS, ON_CYGWIN, LIBC
tasks = LazyObject(collections.deque, globals(), 'tasks')
@ -134,9 +134,6 @@ else:
# give_terminal_to from bash 4.3 source, jobs.c, line 4030
# this will give the terminal to the process group pgid
if ON_CYGWIN:
_libc = LazyObject(lambda: ctypes.CDLL('cygwin1.dll'),
globals(), '_libc')
# on cygwin, signal.pthread_sigmask does not exist in Python, even
# though pthread_sigmask is defined in the kernel. thus, we use
# ctypes to mimic the calls in the "normal" version below.
@ -145,16 +142,16 @@ else:
if st is not None and os.isatty(st):
omask = ctypes.c_ulong()
mask = ctypes.c_ulong()
_libc.sigemptyset(ctypes.byref(mask))
LIBC.sigemptyset(ctypes.byref(mask))
for i in _block_when_giving:
_libc.sigaddset(ctypes.byref(mask), ctypes.c_int(i))
_libc.sigemptyset(ctypes.byref(omask))
_libc.sigprocmask(ctypes.c_int(signal.SIG_BLOCK),
ctypes.byref(mask),
ctypes.byref(omask))
_libc.tcsetpgrp(ctypes.c_int(st), ctypes.c_int(pgid))
_libc.sigprocmask(ctypes.c_int(signal.SIG_SETMASK),
ctypes.byref(omask), None)
LIBC.sigaddset(ctypes.byref(mask), ctypes.c_int(i))
LIBC.sigemptyset(ctypes.byref(omask))
LIBC.sigprocmask(ctypes.c_int(signal.SIG_BLOCK),
ctypes.byref(mask),
ctypes.byref(omask))
LIBC.tcsetpgrp(ctypes.c_int(st), ctypes.c_int(pgid))
LIBC.sigprocmask(ctypes.c_int(signal.SIG_SETMASK),
ctypes.byref(omask), None)
else:
def _give_terminal_to(pgid):
st = _shell_tty()

View file

@ -1,7 +1,7 @@
"""Lazy imports that may apply across the xonsh package."""
import importlib
from xonsh.platform import ON_WINDOWS
from xonsh.platform import ON_WINDOWS, ON_DARWIN
from xonsh.lazyasd import LazyObject, lazyobject
pygments = LazyObject(lambda: importlib.import_module('pygments'),
@ -69,6 +69,15 @@ def winutils():
return m
@lazyobject
def macutils():
if ON_DARWIN:
import xonsh.macutils as m
else:
m = None
return m
@lazyobject
def terminal256():
return importlib.import_module('pygments.formatters.terminal256')

22
xonsh/macutils.py Normal file
View file

@ -0,0 +1,22 @@
"""Provides some Mac / Darwin based utility functions for xonsh."""
from ctypes import c_uint, byref, create_string_buffer
from xonsh.platform import LIBC
def sysctlbyname(name, return_str=True):
"""Gets a sysctrl value by name. If return_str is true, this will return
a string representation, else it will return the raw value.
"""
# forked from https://gist.github.com/pudquick/581a71425439f2cf8f09
size = c_uint(0)
# Find out how big our buffer will be
LIBC.sysctlbyname(name, None, byref(size), None, 0)
# Make the buffer
buf = create_string_buffer(size.value)
# Re-run, but provide the buffer
LIBC.sysctlbyname(name, buf, byref(size), None, 0)
if return_str:
return buf.value
else:
return buf.raw

View file

@ -4,6 +4,7 @@ on a platform.
"""
import os
import sys
import ctypes
import signal
import pathlib
import builtins
@ -55,6 +56,11 @@ ON_BSD = LazyBool(lambda: ON_FREEBSD or ON_NETBSD,
globals(), 'ON_BSD')
"""``True`` if on a BSD operating system, else ``False``."""
@lazybool
def ON_BEOS():
"""True if we are on BeOS or Haiku."""
return sys.platform == 'beos5' or sys.platform == 'haiku1'
#
# Python & packages
@ -361,3 +367,59 @@ def PATH_DEFAULT():
else:
pd = ()
return pd
#
# libc
#
@lazyobject
def LIBC():
"""The platform dependent libc implementation."""
if ON_DARWIN:
libc = ctypes.CDLL(ctypes.util.find_library("c"))
elif ON_CYGWIN:
libc = ctypes.CDLL('cygwin1.dll')
elif ON_BSD:
try:
libc = ctypes.CDLL('libc.so')
except AttributeError:
libc = None
except OSError:
# OS X; can't use ctypes.util.find_library because that creates
# a new process on Linux, which is undesirable.
try:
libc = ctypes.CDLL('libc.dylib')
except OSError:
libc = None
elif ON_POSIX:
try:
libc = ctypes.CDLL('libc.so')
except AttributeError:
libc = None
except OSError:
# Debian and derivatives do the wrong thing because /usr/lib/libc.so
# is a GNU ld script rather than an ELF object. To get around this, we
# have to be more specific.
# We don't want to use ctypes.util.find_library because that creates a
# new process on Linux. We also don't want to try too hard because at
# this point we're already pretty sure this isn't Linux.
try:
libc = ctypes.CDLL('libc.so.6')
except OSError:
libc = None
if not hasattr(libc, 'sysinfo'):
# Not Linux.
libc = None
elif ON_WINDOWS:
if hasattr(ctypes, 'windll') and hasattr(ctypes.windll, 'kernel32'):
libc = ctypes.windll.kernel32
else:
try:
# Windows CE uses the cdecl calling convention.
libc = ctypes.CDLL('coredll.lib')
except (AttributeError, OSError):
libc = None
elif ON_BEOS:
libc = ctypes.CDLL('libroot.so')
else:
libc = None
return libc

View file

@ -0,0 +1,2 @@
# amalgamate
# amalgamate end

258
xonsh/xoreutils/uptime.py Normal file
View file

@ -0,0 +1,258 @@
"""
Provides a cross-platform way to figure out the system uptime.
Should work on damned near any operating system you can realistically expect
to be asked to write Python code for.
If this module is invoked as a stand-alone script, it will print the current
uptime in a human-readable format, or display an error message if it can't,
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 os
import sys
import time
import ctypes
import struct
import xonsh.platform as xp
import xonsh.lazyimps as xlimps
_BOOTTIME = None
def _uptime_osx():
"""Returns the uptime on mac / darwin."""
global _BOOTTIME
bt = xlimps.macutils.sysctlbyname("kern.boottime", return_str=False)
bt = struct.unpack('@LL', bt)
bt = bt[0] + bt[1]*1e-6
if bt == 0.0:
return None
_BOOTTIME = bt
return time.time() - bt
def _uptime_linux():
"""Returns uptime in seconds or None, on Linux."""
# With procfs
try:
with open('/proc/uptime', 'r') as f:
up = float(f.readline().split()[0])
return up
except (IOError, 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():
"""A way to figure out the boot time directly on Linux."""
global _BOOTTIME
try:
with open('/proc/stat', 'r') as f:
for line in f:
if line.startswith('btime'):
_BOOTTIME = float(line.split()[1])
return _BOOTTIME
except (IOError, IndexError):
return None
def _uptime_amiga():
"""Returns uptime in seconds or None, on AmigaOS."""
global _BOOTTIME
try:
_BOOTTIME = os.stat('RAM:').st_ctime
return time.time() - _BOOTTIME
except (NameError, OSError):
return None
def _uptime_beos():
"""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.
def _uptime_bsd():
"""Returns uptime in seconds or None, on BSD (including OS X)."""
global _BOOTTIME
if not hasattr(xp.LIBC, 'sysctlbyname'):
# Not BSD.
return None
# Determine how much space we need for the response.
sz = ctypes.c_uint(0)
libc.sysctlbyname('kern.boottime', None, ctypes.byref(sz), None, 0)
if sz.value != struct.calcsize('@LL'):
# Unexpected, let's give up.
return None
# For real now.
buf = ctypes.create_string_buffer(sz.value)
libc.sysctlbyname('kern.boottime', buf, ctypes.byref(sz), None, 0)
sec, usec = struct.unpack('@LL', buf.raw)
# OS X disagrees what that second value is.
if usec > 1000000:
usec = 0.
_BOOTTIME = sec + usec / 1000000.
up = time.time() - _BOOTTIME
if up < 0:
up = None
return up
def _uptime_minix():
"""Returns uptime in seconds or None, on MINIX."""
try:
with open('/proc/uptime', 'r') as f:
up = float(f.read())
return up
except (IOError, ValueError):
return None
def _uptime_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
# talking about it somewhere.
try:
# The time file holds one 32-bit number representing the sec-
# onds since start of epoch and three 64-bit numbers, repre-
# senting nanoseconds since start of epoch, clock ticks, and
# clock frequency.
# -- cons(3)
with open('/dev/time', 'r') as f:
s, ns, ct, cf = f.read().split()
return float(ct) / float(cf)
except (IOError, ValueError):
return None
def _uptime_solaris():
"""Returns uptime in seconds or None, on Solaris."""
global _BOOTTIME
try:
kstat = ctypes.CDLL('libkstat.so')
except (AttributeError, OSError):
return 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
# Constant
KSTAT_STRLEN = 31 # According to every kstat.h I could find.
# Data structures
class anon_union(ctypes.Union):
# The ``value'' union in kstat_named_t actually has a bunch more
# members, but we're only using it for boot_time, so we only need
# the padding and the one we're actually using.
_fields_ = [('c', ctypes.c_char * 16),
('time', ctypes.c_int)]
class kstat_named_t(ctypes.Structure):
_fields_ = [('name', ctypes.c_char * KSTAT_STRLEN),
('data_type', ctypes.c_char),
('value', anon_union)]
# Function signatures
kstat.kstat_open.restype = ctypes.c_void_p
kstat.kstat_lookup.restype = ctypes.c_void_p
kstat.kstat_lookup.argtypes = [ctypes.c_void_p,
ctypes.c_char_p,
ctypes.c_int,
ctypes.c_char_p]
kstat.kstat_read.restype = ctypes.c_int
kstat.kstat_read.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
kstat.kstat_data_lookup.restype = ctypes.POINTER(kstat_named_t)
kstat.kstat_data_lookup.argtypes = [ctypes.c_void_p,
ctypes.c_char_p]
# Now, let's do something useful.
# Initialise kstat control structure.
kc = kstat.kstat_open()
if not kc:
return None
# We're looking for unix:0:system_misc:boot_time.
ksp = kstat.kstat_lookup(kc, 'unix', 0, 'system_misc')
if ksp and kstat.kstat_read(kc, ksp, None) != -1:
data = kstat.kstat_data_lookup(ksp, 'boot_time')
if data:
_BOOTTIME = data.contents.value.time
# Clean-up.
kstat.kstat_close(kc)
if _BOOTTIME is not None:
return time.time() - _BOOTTIME
return None
def _uptime_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
except (NameError, OSError):
return None
def _uptime_windows():
"""
Returns uptime in seconds or None, on Windows. Warning: may return
incorrect answers after 49.7 days on versions older than Vista.
"""
if hasattr(xp.LIBC, 'GetTickCount64'):
# Vista/Server 2008 or later.
xp.LIBC.GetTickCount64.restype = ctypes.c_uint64
return xp.LIBC.GetTickCount64() / 1000.
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.
return None
def uptime():
"""Returns uptime in seconds if even remotely possible, or None if not."""
if _BOOTTIME is not None:
return time.time() - _BOOTTIME
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}.get(sys.platform, _uptime_bsd)() or \
_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()
def boottime():
"""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