summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2019-12-30 01:42:06 -0800
committerGiampaolo Rodola <g.rodola@gmail.com>2019-12-30 01:42:06 -0800
commit84e63f12355bd96f66283e6232184b924927ff64 (patch)
treed48128cffebc690f4cf634c86f0c7b2050f3524f
parent70a141cf61bab7c40662ae243d2bff06f6d8bdd7 (diff)
parent283faf2eef04b0ed6dd05f53c84c37bee017ea91 (diff)
downloadpsutil-84e63f12355bd96f66283e6232184b924927ff64.tar.gz
Merge branch 'master' of github.com:giampaolo/psutil
-rw-r--r--CREDITS13
-rw-r--r--HISTORY.rst28
-rw-r--r--README.rst28
-rw-r--r--docs/index.rst52
-rw-r--r--psutil/__init__.py2
-rw-r--r--psutil/_common.py36
-rw-r--r--psutil/_pslinux.py1
-rw-r--r--psutil/_pssunos.py3
-rw-r--r--psutil/_psutil_bsd.c6
-rw-r--r--psutil/_psutil_sunos.c19
-rw-r--r--psutil/arch/solaris/environ.c1
-rw-r--r--psutil/tests/README.rst2
-rw-r--r--psutil/tests/__init__.py5
-rwxr-xr-xpsutil/tests/test_linux.py1
-rwxr-xr-xsetup.py65
15 files changed, 225 insertions, 37 deletions
diff --git a/CREDITS b/CREDITS
index 330f4f99..988af1fe 100644
--- a/CREDITS
+++ b/CREDITS
@@ -631,9 +631,9 @@ N: Erwan Le Pape
W: https://github.com/erwan-le-pape
I: 1570
-N: vser1
+N: Étienne Servais
W: https://github.com/vser1
-I: 1607
+I: 1607, 1637
N: Bernát Gábor
W: https://github.com/gaborbernat
@@ -647,3 +647,12 @@ N: Riccardo Schirone
W: https://github.com/ret2libc
C: Milano, Italy
I: 1616
+
+N: Po-Chuan Hsieh
+W: https://github.com/sunpoet
+C: Taiwan
+I: 1646
+
+N: Javad Karabi
+W: https://github.com/karabijavad
+I: 1648
diff --git a/HISTORY.rst b/HISTORY.rst
index 5cef298c..16d18c08 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,9 +1,35 @@
*Bug tracker at https://github.com/giampaolo/psutil/issues*
+5.6.8 (unreleased)
+==================
+
+XXXX-XX-XX
+
+**Enhancements**
+
+- 1648_: [Linux] sensors_temperatures() looks into an additional /sys/device/
+ directory for additional data. (patch by Javad Karabi)
+
+**Bug fixes**
+
+- 1637_: [SunOS] Add partial support for old SunOS 5.10 Update 0 to 3.
+- 1642_: [SunOS] querying basic info for PID 0 results in FileNotFoundError.
+- 1646_: [FreeBSD] many Process methods may cause a segfault on FreeBSD 12.0
+ due to a backward incompatible change in a C type introduced in 12.0.
+
+5.6.7
+=====
+
+2019-11-26
+
+**Bug fixes**
+
+- 1630_: [Windows] can't compile source distribution due to C syntax error.
+
5.6.6
=====
-XXXX-XX-XX
+2019-11-25
**Bug fixes**
diff --git a/README.rst b/README.rst
index 44b88e7a..639dfb06 100644
--- a/README.rst
+++ b/README.rst
@@ -98,28 +98,28 @@ psutil currently supports the following platforms:
...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy <http://pypy.org/>`__ is also known to work.
-Professional support
-====================
+psutil for enterprise
+=====================
.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png
- :width: 100
+ :width: 150
:alt: Tidelift
:target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
.. list-table::
- :widths: 10 100
+ :widths: 10 150
* - |tideliftlogo|
- - Professional support for psutil is available as part of the
- `Tidelift Subscription`_.
- Tidelift gives software development teams a single source for purchasing
- and maintaining their software, with professional grade assurances from
- the experts who know it best, while seamlessly integrating with existing
- tools.
- By subscribing you will help me (`Giampaolo Rodola`_) support psutil
- future development. Alternatively consider making a small `donation`_.
+ - The maintainer of psutil and thousands of other packages are working
+ with Tidelift to deliver commercial support and maintenance for the open
+ source dependencies you use to build your applications. Save time,
+ reduce risk, and improve code health, while paying the maintainers of
+ the exact dependencies you use.
+ `Learn more <https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=enterprise&utm_term=repo>`__.
-.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
+ By subscribing to Tidelift you will help me (`Giampaolo Rodola`_) support
+ psutil future development. Alternatively consider making a small
+ `donation`_.
Security
========
@@ -515,3 +515,5 @@ Windows services
.. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html
.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
.. _Tidelift security contact: https://tidelift.com/security
+.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
+
diff --git a/docs/index.rst b/docs/index.rst
index 74a4c36e..5ed3bb7c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -42,21 +42,45 @@ Supported Python versions are **2.6**, **2.7** and **3.4+**.
The psutil documentation you're reading is distributed as a single HTML page.
+
+Professional support
+--------------------
+
+.. image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png
+ :width: 80px
+ :align: left
+
+Professional support for psutil is available as part of the `Tidelift Subscription`_.
+Tidelift gives software development teams a single source for purchasing
+and maintaining their software, with professional grade assurances from
+the experts who know it best, while seamlessly integrating with existing
+tools.
+By subscribing you will help me (`Giampaolo Rodola`_) support psutil
+future development. Alternatively consider making a small `donation`_.
+To report a security vulnerability, please use the `Tidelift security
+contact`_. Tidelift will coordinate the fix and disclosure.
+
Install
-------
-The easiest way to install psutil is via ``pip``::
+Linux Ubuntu / Debian::
- pip install psutil
+ sudo apt-get install gcc python3-dev
+ sudo pip3 install psutil
-On UNIX this requires a C compiler (e.g. gcc) installed. On Windows pip will
-automatically retrieve a pre-compiled wheel version from
-`PyPI repository <https://pypi.org/project/psutil>`__.
-Alternatively, see more detailed
+Linux Redhat::
+
+ sudo yum install gcc python3-devel
+ sudo pip3 install psutil
+
+Windows::
+
+ pip3 install psutil
+
+For other platforms see more detailed
`install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
instructions.
-
System related functions
========================
@@ -276,7 +300,7 @@ Memory
Return statistics about system memory usage as a named tuple including the
following fields, expressed in bytes. Main metrics:
- - **total**: total physical memory.
+ - **total**: total physical memory (exclusive swap).
- **available**: the memory that can be given instantly to processes without
the system going into swap.
This is calculated by summing different memory values depending on the
@@ -2635,6 +2659,14 @@ take a look at the `development guide`_.
Timeline
========
+- 2019-11-26:
+ `5.6.7 <https://pypi.org/project/psutil/5.6.7/#files>`__ -
+ `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#567>`__ -
+ `diff <https://github.com/giampaolo/psutil/compare/release-5.6.6...release-5.6.7#files_bucket>`__
+- 2019-11-25:
+ `5.6.6 <https://pypi.org/project/psutil/5.6.6/#files>`__ -
+ `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#566>`__ -
+ `diff <https://github.com/giampaolo/psutil/compare/release-5.6.5...release-5.6.6#files_bucket>`__
- 2019-11-06:
`5.6.5 <https://pypi.org/project/psutil/5.6.5/#files>`__ -
`what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#565>`__ -
@@ -2948,11 +2980,13 @@ Timeline
.. _`cpu_distribution.py`: https://github.com/giampaolo/psutil/blob/master/scripts/cpu_distribution.py
.. _`development guide`: https://github.com/giampaolo/psutil/blob/master/docs/DEVGUIDE.rst
.. _`disk_usage.py`: https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py
+.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
.. _`enums`: https://docs.python.org/3/library/enum.html#module-enum
.. _`fans.py`: https://github.com/giampaolo/psutil/blob/master/scripts/fans.py
.. _`GetDriveType`: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getdrivetypea
.. _`getfsstat`: http://www.manpagez.com/man/2/getfsstat/
.. _`GetPriorityClass`: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getpriorityclass
+.. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html
.. _`hash`: https://docs.python.org/3/library/functions.html#hash
.. _`ifconfig.py`: https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py
.. _`ioprio_get`: https://linux.die.net/man/2/ioprio_get
@@ -2994,3 +3028,5 @@ Timeline
.. _`subprocess.Popen`: https://docs.python.org/3/library/subprocess.html#subprocess.Popen
.. _`temperatures.py`: https://github.com/giampaolo/psutil/blob/master/scripts/temperatures.py
.. _`TerminateProcess`: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-terminateprocess
+.. _Tidelift security contact: https://tidelift.com/security
+.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 4df2fec0..b267239e 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -222,7 +222,7 @@ __all__ = [
__all__.extend(_psplatform.__extra__all__)
__author__ = "Giampaolo Rodola'"
-__version__ = "5.6.6"
+__version__ = "5.6.7"
version_info = tuple([int(num) for num in __version__.split('.')])
_timer = getattr(time, 'monotonic', time.time)
diff --git a/psutil/_common.py b/psutil/_common.py
index 126d9d6f..2f74460b 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -23,6 +23,7 @@ from collections import namedtuple
from socket import AF_INET
from socket import SOCK_DGRAM
from socket import SOCK_STREAM
+
try:
from socket import AF_INET6
except ImportError:
@@ -37,6 +38,7 @@ if sys.version_info >= (3, 4):
else:
enum = None
+
# can't take it from _common.py as this script is imported by setup.py
PY3 = sys.version_info[0] == 3
@@ -64,7 +66,7 @@ __all__ = [
'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize',
'parse_environ_block', 'path_exists_strict', 'usage_percent',
'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers",
- 'bytes2human', 'conn_to_ntuple',
+ 'bytes2human', 'conn_to_ntuple', 'hilite',
]
@@ -649,3 +651,35 @@ if PY3:
else:
def decode(s):
return s
+
+
+def _term_supports_colors(file=sys.stdout):
+ if hasattr(_term_supports_colors, "ret"):
+ return _term_supports_colors.ret
+ try:
+ import curses
+ assert file.isatty()
+ curses.setupterm()
+ assert curses.tigetnum("colors") > 0
+ except Exception:
+ _term_supports_colors.ret = False
+ return False
+ else:
+ _term_supports_colors.ret = True
+ return _term_supports_colors.ret
+
+
+def hilite(s, ok=True, bold=False):
+ """Return an highlighted version of 'string'."""
+ if not _term_supports_colors():
+ return s
+ attr = []
+ if ok is None: # no color
+ pass
+ elif ok: # green
+ attr.append('32')
+ else: # red
+ attr.append('31')
+ if bold:
+ attr.append('1')
+ return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s)
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index a56ead36..e95581cc 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -1206,6 +1206,7 @@ def sensors_temperatures():
# https://github.com/giampaolo/psutil/issues/971
# https://github.com/nicolargo/glances/issues/1060
basenames.extend(glob.glob('/sys/class/hwmon/hwmon*/device/temp*_*'))
+ basenames.extend(glob.glob('/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*'))
basenames = sorted(set([x.split('_')[0] for x in basenames]))
for base in basenames:
diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py
index 2aa2a866..9f0cac26 100644
--- a/psutil/_pssunos.py
+++ b/psutil/_pssunos.py
@@ -400,6 +400,9 @@ class Process(object):
@wrap_exceptions
@memoize_when_activated
def _proc_basic_info(self):
+ if self.pid == 0 and not \
+ os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)):
+ raise AccessDenied(self.pid)
ret = cext.proc_basic_info(self.pid, self._procfs_path)
assert len(ret) == len(proc_info_map)
return ret
diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c
index 723d6198..94088d73 100644
--- a/psutil/_psutil_bsd.c
+++ b/psutil/_psutil_bsd.c
@@ -271,7 +271,11 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
// Return a single big tuple with all process info.
py_retlist = Py_BuildValue(
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031
+ "(lillllllLdllllddddlllllbO)",
+#else
"(lillllllidllllddddlllllbO)",
+#endif
#ifdef PSUTIL_FREEBSD
//
(long)kp.ki_ppid, // (long) ppid
@@ -285,7 +289,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
(long)kp.ki_groups[0], // (long) effective gid
(long)kp.ki_svuid, // (long) saved gid
//
- kp.ki_tdev, // (int) tty nr
+ kp.ki_tdev, // (int or long long) tty nr
PSUTIL_TV2DOUBLE(kp.ki_start), // (double) create time
// ctx switches
kp.ki_rusage.ru_nvcsw, // (long) ctx switches (voluntary)
diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c
index 31d6f364..bcfb448f 100644
--- a/psutil/_psutil_sunos.c
+++ b/psutil/_psutil_sunos.c
@@ -12,12 +12,8 @@
/* fix compilation issue on SunOS 5.10, see:
* https://github.com/giampaolo/psutil/issues/421
* https://github.com/giampaolo/psutil/issues/1077
- * http://us-east.manta.joyent.com/jmc/public/opensolaris/ARChive/PSARC/2010/111/materials/s10ceval.txt
- *
- * Because LEGACY_MIB_SIZE defined in the same file there is no way to make autoconfiguration =\
*/
-#define NEW_MIB_COMPLIANT 1
#define _STRUCTURED_PROC 1
#include <Python.h>
@@ -44,6 +40,14 @@
#include <sys/tihdr.h>
#include <stropts.h>
#include <inet/tcp.h>
+#ifndef NEW_MIB_COMPLIANT
+/*
+ * Solaris introduced NEW_MIB_COMPLIANT macro with Update 4.
+ * See https://github.com/giampaolo/psutil/issues/421
+ * Prior to Update 4, one has to include mib2 by hand.
+ */
+#include <inet/mib2.h>
+#endif
#include <arpa/inet.h>
#include <net/if.h>
#include <math.h> // fabs()
@@ -1747,7 +1751,14 @@ void init_psutil_sunos(void)
PyModule_AddIntConstant(module, "SSTOP", SSTOP);
PyModule_AddIntConstant(module, "SIDL", SIDL);
PyModule_AddIntConstant(module, "SONPROC", SONPROC);
+#ifdef SWAIT
PyModule_AddIntConstant(module, "SWAIT", SWAIT);
+#else
+ /* sys/proc.h started defining SWAIT somewhere
+ * after Update 3 and prior to Update 5 included.
+ */
+ PyModule_AddIntConstant(module, "SWAIT", 0);
+#endif
PyModule_AddIntConstant(module, "PRNODEV", PRNODEV); // for process tty
diff --git a/psutil/arch/solaris/environ.c b/psutil/arch/solaris/environ.c
index 1af4c129..eb0fa2ab 100644
--- a/psutil/arch/solaris/environ.c
+++ b/psutil/arch/solaris/environ.c
@@ -6,7 +6,6 @@
* Functions specific for Process.environ().
*/
-#define NEW_MIB_COMPLIANT 1
#define _STRUCTURED_PROC 1
#include <Python.h>
diff --git a/psutil/tests/README.rst b/psutil/tests/README.rst
index 9cefb775..b647d513 100644
--- a/psutil/tests/README.rst
+++ b/psutil/tests/README.rst
@@ -7,7 +7,7 @@ Instructions for running tests
python -m psutil.tests --install-deps # install test deps
python -m psutil.tests
- As a "developer", if you have a copy of the source code and you whish to hack
+ As a "developer", if you have a copy of the source code and you wish to hack
on psutil::
make setup-dev-env # install test deps (+ other things)
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 6c629368..767524af 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -153,9 +153,10 @@ ASCII_FS = sys.getfilesystemencoding().lower() in ('ascii', 'us-ascii')
# --- paths
-ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
+ROOT_DIR = os.path.realpath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
SCRIPTS_DIR = os.path.join(ROOT_DIR, 'scripts')
-HERE = os.path.abspath(os.path.dirname(__file__))
+HERE = os.path.realpath(os.path.dirname(__file__))
# --- support
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index 4d9cea92..f503b384 100755
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -1616,6 +1616,7 @@ class TestSensorsTemperatures(unittest.TestCase):
elif path == '/sys/class/thermal/thermal_zone0/trip_point*':
return ['/sys/class/thermal/thermal_zone1/trip_point_0_type',
'/sys/class/thermal/thermal_zone1/trip_point_0_temp']
+ return []
orig_open = open
patch_point = 'builtins.open' if PY3 else '__builtin__.open'
diff --git a/setup.py b/setup.py
index 99818ad5..a482e427 100755
--- a/setup.py
+++ b/setup.py
@@ -6,6 +6,7 @@
"""Cross-platform lib for process and system monitoring in Python."""
+from __future__ import print_function
import contextlib
import io
import os
@@ -14,6 +15,7 @@ import shutil
import sys
import tempfile
import warnings
+import re
with warnings.catch_warnings():
warnings.simplefilter("ignore")
@@ -26,12 +28,13 @@ with warnings.catch_warnings():
HERE = os.path.abspath(os.path.dirname(__file__))
-# ...so we can import _common.py
+# ...so we can import _common.py and _compat.py
sys.path.insert(0, os.path.join(HERE, "psutil"))
from _common import AIX # NOQA
from _common import BSD # NOQA
from _common import FREEBSD # NOQA
+from _common import hilite # NOQA
from _common import LINUX # NOQA
from _common import MACOS # NOQA
from _common import NETBSD # NOQA
@@ -39,6 +42,8 @@ from _common import OPENBSD # NOQA
from _common import POSIX # NOQA
from _common import SUNOS # NOQA
from _common import WINDOWS # NOQA
+from _compat import PY3 # NOQA
+from _compat import which # NOQA
macros = []
@@ -104,6 +109,13 @@ def silenced_output(stream_name):
setattr(sys, stream_name, orig)
+def missdeps(msg):
+ s = hilite("C compiler or Python headers are not installed ", ok=False)
+ s += hilite("on this system. Try to run:\n", ok=False)
+ s += hilite(msg, ok=False, bold=True)
+ print(s, file=sys.stderr)
+
+
if WINDOWS:
def get_winver():
maj, min = sys.getwindowsversion()[0:2]
@@ -266,10 +278,28 @@ if POSIX:
define_macros=macros,
sources=sources)
if SUNOS:
+ def get_sunos_update():
+ # See https://serverfault.com/q/524883
+ # for an explanation of Solaris /etc/release
+ with open('/etc/release') as f:
+ update = re.search(r'(?<=s10s_u)[0-9]{1,2}', f.readline())
+ if update is None:
+ return 0
+ else:
+ return int(update.group(0))
+
posix_extension.libraries.append('socket')
if platform.release() == '5.10':
+ # Detect Solaris 5.10, update >= 4, see:
+ # https://github.com/giampaolo/psutil/pull/1638
+ if get_sunos_update() >= 4:
+ # MIB compliancy starts with SunOS 5.10 Update 4:
+ posix_extension.define_macros.append(('NEW_MIB_COMPLIANT', 1))
posix_extension.sources.append('psutil/arch/solaris/v10/ifaddrs.c')
posix_extension.define_macros.append(('PSUTIL_SUNOS10', 1))
+ else:
+ # Other releases are by default considered to be new mib compliant.
+ posix_extension.define_macros.append(('NEW_MIB_COMPLIANT', 1))
elif AIX:
posix_extension.sources.append('psutil/arch/aix/ifaddrs.c')
@@ -346,7 +376,38 @@ def main():
extras_require=extras_require,
zip_safe=False,
)
- setup(**kwargs)
+ success = False
+ try:
+ setup(**kwargs)
+ success = True
+ finally:
+ if not success and POSIX and not which('gcc'):
+ py3 = "3" if PY3 else ""
+ if LINUX:
+ if which('dpkg'):
+ missdeps("sudo apt-get install gcc python%s-dev" % py3)
+ elif which('rpm'):
+ missdeps("sudo yum install gcc python%s-devel" % py3)
+ elif MACOS:
+ print(hilite("XCode (https://developer.apple.com/xcode/) "
+ "is not installed"), ok=False, file=sys.stderr)
+ elif FREEBSD:
+ missdeps("pkg install gcc python%s" % py3)
+ elif OPENBSD:
+ missdeps("pkg_add -v gcc python%s" % py3)
+ elif NETBSD:
+ missdeps("pkgin install gcc python%s" % py3)
+ elif SUNOS:
+ missdeps("sudo ln -s /usr/bin/gcc /usr/local/bin/cc && "
+ "pkg install gcc")
+ elif not success and WINDOWS:
+ if PY3:
+ ur = "http://www.visualstudio.com/en-au/news/vs2015-preview-vs"
+ else:
+ ur = "http://www.microsoft.com/en-us/download/"
+ ur += "details.aspx?id=44266"
+ print(hilite("VisualStudio is not installed; get it from %s" % ur),
+ ok=False, file=sys.stderr)
if __name__ == '__main__':