summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml16
-rw-r--r--CHANGES27
-rw-r--r--README2
-rw-r--r--debian/.git-dpm11
-rw-r--r--debian/changelog26
-rw-r--r--debian/compat2
-rw-r--r--debian/control12
-rw-r--r--debian/copyright11
-rw-r--r--debian/patches/0001-fix-build.patch31
-rw-r--r--debian/patches/series1
-rw-r--r--debian/watch2
-rw-r--r--docs/_templates/sidebarintro.html4
-rw-r--r--docs/_templates/sidebarlogo.html4
-rw-r--r--docs/backends.rst7
-rw-r--r--docs/conf.py2
-rw-r--r--docs/configuring.rst14
-rw-r--r--docs/contributing.rst10
-rw-r--r--docs/faq.rst4
-rw-r--r--docs/getting.rst8
-rw-r--r--docs/getting_nivisa.rst2
-rw-r--r--docs/resources.rst2
-rw-r--r--docs/rvalues.rst13
-rw-r--r--docs/shell.rst102
-rw-r--r--pyvisa/compat/__init__.py101
-rw-r--r--pyvisa/compat/check_output.py35
-rw-r--r--pyvisa/compat/struct.py3
-rw-r--r--pyvisa/constants.py2
-rw-r--r--pyvisa/ctwrapper/functions.py4
-rw-r--r--pyvisa/ctwrapper/highlevel.py4
-rw-r--r--pyvisa/errors.py29
-rw-r--r--pyvisa/highlevel.py37
-rw-r--r--pyvisa/resources/messagebased.py328
-rw-r--r--pyvisa/resources/resource.py12
-rw-r--r--pyvisa/shell.py115
-rw-r--r--pyvisa/testsuite/__init__.py23
-rw-r--r--pyvisa/testsuite/test_errors.py35
-rw-r--r--pyvisa/testsuite/test_rname.py5
-rw-r--r--pyvisa/testsuite/test_util.py120
-rw-r--r--pyvisa/util.py210
-rw-r--r--setup.py33
-rw-r--r--tox.ini4
-rw-r--r--visa.py26
42 files changed, 1133 insertions, 306 deletions
diff --git a/.travis.yml b/.travis.yml
index 28f009c..291df6f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,14 +1,22 @@
language: python
+branches:
+ only:
+ # This is where pull requests from "bors r+" are built.
+ - staging
+ # This is where pull requests from "bors try" are built.
+ - trying
+ # Build master too
+ - master
+
python:
- - "2.6"
- "2.7"
- - "3.2"
- - "3.3"
- "3.4"
+ - "3.5"
+ - "3.6"
install:
- - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then pip install unittest2; fi
+ - if [ $TRAVIS_PYTHON_VERSION == '2.7' ] || [ $TRAVIS_PYTHON_VERSION == '3.6' ]; then pip install numpy; fi
- pip install coverage coveralls
script:
diff --git a/CHANGES b/CHANGES
index 3c74939..0c53b45 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,31 @@ PyVISA Changelog
================
+1.9 (2017-02-25)
+----------------
+
+- Drop support for Python 2.6, 3.2 and 3.3 PR #300
+- add the missing read_binary_values and read_ascii_values (PR #301)
+- deprecate old methods in MessageBased (ask, read_values, query_values,
+ write_values, ask_delay) (PR #301)
+- add support for hp headers in binary data (PR #301)
+- fix encoding issue in write_ascii_values (PR #301)
+- use import to load backend rather than pkgutil.iter_modules. This allows
+ PyVISA to support PyInstaller PR #307
+- improvements to the visa shell: attributes type conversion (PR #299),
+ termchar command (PR #285), timeout command (PR #284),
+ support for non-default backend (PR #283), console script pyvisa-shell
+ (PR #286)
+- improve speed for large data transfer by using bytearray instead of bytes
+ (PR #282)
+- make Resource a context manager closing it. (PR #255)
+- add 64 bits version of registry based functions (PR #278)
+- make exceptions pickable (PR #249)
+- add resource_name to the output of parse_resource_extended (PR #238)
+- fix wait_on_event behavior in case of timeout (PR #234)
+- allow selecting the backend using the PYVISA_LIBRARY env var (PR #195)
+
+
1.8 (2015-08-24)
----------------
@@ -98,7 +123,7 @@ PyVISA Changelog
- Better debug info for binary libraries.
- Fixed exceptions formatting
(thanks Matthew94)
-
+
1.6 (2014-09-28)
diff --git a/README b/README
index 480038f..4d92397 100644
--- a/README
+++ b/README
@@ -44,7 +44,7 @@ Requirements
------------
- VISA (tested with NI-VISA 3.2, WinXP, from www.ni.com/visa)
-- Python (tested with 2.6 and 3.2+)
+- Python (tested with 2.7 and 3.4+)
Installation
diff --git a/debian/.git-dpm b/debian/.git-dpm
deleted file mode 100644
index 67f3f17..0000000
--- a/debian/.git-dpm
+++ /dev/null
@@ -1,11 +0,0 @@
-# see git-dpm(1) from git-dpm package
-a5c3cbf9b15ec3f61547dc5ba6d1d3c427c29f70
-a5c3cbf9b15ec3f61547dc5ba6d1d3c427c29f70
-a5c3cbf9b15ec3f61547dc5ba6d1d3c427c29f70
-a5c3cbf9b15ec3f61547dc5ba6d1d3c427c29f70
-pyvisa_1.8.orig.tar.gz
-9f38c897d303ea49a546b58d6b356799fbcd2e84
-423435
-debianTag="debian/%e%v"
-patchedTag="patched/%e%v"
-upstreamTag="upstream/%e%u"
diff --git a/debian/changelog b/debian/changelog
index faed03f..eec2cbe 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,27 @@
+pyvisa (1.9.0-1) unstable; urgency=medium
+
+ * New upstream release
+
+ [ Ondřej Nový ]
+ * debian/control: Set Vcs-* to salsa.debian.org
+ * debian/copyright: Use https protocol in Format field
+ * debian/control: Deprecating priority extra as per policy 4.0.1
+ * debian/changelog: Remove trailing whitespaces
+ * debian/control: Remove ancient X-Python3-Version field
+
+ [ Ruben Undheim ]
+ * Updated homepage URL, watch URL and source url in debian/copyright
+ * debian/copyright: Updates according to changes in new release
+ * debian/compat: compat level set to 11
+ * debian/control:
+ - debhelper >= 11
+ - Standards version 4.2.0
+ * Deleted debian/.git-dpm
+ * debian/patches/0001-fix-build.patch:
+ - Fix an issue popping up when building on latest sid
+
+ -- Ruben Undheim <ruben.undheim@gmail.com> Sun, 05 Aug 2018 21:12:47 +0200
+
pyvisa (1.8-4) unstable; urgency=low
* Moved python-pyvisa-py back from Depends to Recommends. (Closes: #836963)
@@ -34,7 +58,7 @@ pyvisa (1.8-1) unstable; urgency=low
pyvisa (1.7-1) unstable; urgency=medium
- * New upstream release
+ * New upstream release
* Updated d/copyright with info about new files.
* d/control:
- transitional package pyvisa moved to Section "oldlibs" and description
diff --git a/debian/compat b/debian/compat
index ec63514..b4de394 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-9
+11
diff --git a/debian/control b/debian/control
index 79f40fe..fa18e45 100644
--- a/debian/control
+++ b/debian/control
@@ -3,23 +3,21 @@ Section: python
Priority: optional
Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
Uploaders: Ruben Undheim <ruben.undheim@gmail.com>
-Build-Depends: debhelper (>= 9),
+Build-Depends: debhelper (>= 11),
dh-python,
python-all,
python-setuptools,
python3-all,
python3-setuptools
-Standards-Version: 3.9.8
+Standards-Version: 4.2.0
X-Python-Version: all
-X-Python3-Version: >= 3.1
-Vcs-Browser: https://anonscm.debian.org/cgit/python-modules/packages/pyvisa.git
-Vcs-Git: https://anonscm.debian.org/git/python-modules/packages/pyvisa.git
-Homepage: http://pyvisa.sourceforge.net/
+Vcs-Browser: https://salsa.debian.org/python-team/modules/pyvisa
+Vcs-Git: https://salsa.debian.org/python-team/modules/pyvisa.git
+Homepage: https://pyvisa.readthedocs.io/
Package: pyvisa
Depends: python-pyvisa, ${misc:Depends}
Architecture: all
-Priority: extra
Section: oldlibs
Description: Transitional dummy package for python-pyvisa
This is a transitional dummy package. It can safely be removed.
diff --git a/debian/copyright b/debian/copyright
index 0cae160..593bb84 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,9 +1,9 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: pyvisa
-Source: https://github.com/hgrecco/pyvisa
+Source: https://github.com/pyvisa/pyvisa
Files: *
-Copyright: 2005-2014 PyVISA Authors and contributors
+Copyright: 2005-2018 PyVISA Authors and contributors
2005, 2006, 2007, 2008 Torsten Bronger <bronger@physik.rwth-aachen.de>
2005, 2006, 2007, 2008 Gregor Thalhammer <gth@users.sourceforge.net>
2012, 2013 Florian Bauer
@@ -21,8 +21,7 @@ Files: pyvisa/thirdparty/prettytable.py
Copyright: 2009-2013 Luke Maurits <luke@maurits.id.au>
License: BSD-3-clause
-Files: pyvisa/compat/check_output.py
- pyvisa/compat/nullhandler.py
+Files: pyvisa/compat/nullhandler.py
pyvisa/compat/struct.py
Copyright: 2013, 2015 PSF
License: PSF
@@ -42,7 +41,7 @@ License: BSD-3-clause
Files: debian/*
Copyright: 2013 Simon Richter <sjr@debian.org>
- 2015 Ruben Undheim <ruben.undheim@gmail.com>
+ 2015-2016,2018 Ruben Undheim <ruben.undheim@gmail.com>
License: GPL-2+
diff --git a/debian/patches/0001-fix-build.patch b/debian/patches/0001-fix-build.patch
new file mode 100644
index 0000000..562edcc
--- /dev/null
+++ b/debian/patches/0001-fix-build.patch
@@ -0,0 +1,31 @@
+From: Ruben Undheim <ruben.undheim@gmail.com>
+Date: Sun, 5 Aug 2018 19:28:21 +0000
+Subject: Fix build
+
+---
+ setup.py | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/setup.py b/setup.py
+index a3d05a8..43c0732 100644
+--- a/setup.py
++++ b/setup.py
+@@ -16,13 +16,14 @@ except ImportError:
+
+
+ def read(filename):
+- with open(filename, 'r') as f:
++ with open(filename, 'rb') as f:
+ return f.read()
+
++a2 = read('AUTHORS').decode('utf-8')
++a3 = read('CHANGES').decode('utf-8')
++a1 = read('README').decode('utf-8')
+
+-long_description = '\n\n'.join([read('README'),
+- read('AUTHORS'),
+- read('CHANGES')])
++long_description = '\n\n'.join([a1,a2,a3])
+
+ __doc__ = long_description
+
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..83704e9
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+0001-fix-build.patch
diff --git a/debian/watch b/debian/watch
index c0f5f5a..c427358 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,2 +1,2 @@
version=3
-https://github.com/hgrecco/pyvisa/releases /hgrecco/pyvisa/archive/(\d\S+)\.tar\.(?:bz2|gz|xz)
+https://github.com/pyvisa/pyvisa/releases /pyvisa/pyvisa/archive/(\d\S+)\.tar\.(?:bz2|gz|xz)
diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html
index 92ebbc4..7ccc188 100644
--- a/docs/_templates/sidebarintro.html
+++ b/docs/_templates/sidebarintro.html
@@ -13,8 +13,8 @@ You are currently looking at the documentation of version {{ version }}.
<h3>Useful Links</h3>
<ul>
<li><a href="https://pypi.python.org/pypi/pyvisa/">PyVISA @ PyPI</a></li>
- <li><a href="https://github.com/hgrecco/pyvisa">Code in GitHub</a></li>
- <li><a href="https://github.com/hgrecco/pyvisa/issues">Issue Tracker</a></li>
+ <li><a href="https://github.com/pyvisa/pyvisa">Code in GitHub</a></li>
+ <li><a href="https://github.com/pyvisa/pyvisa/issues">Issue Tracker</a></li>
</ul>
diff --git a/docs/_templates/sidebarlogo.html b/docs/_templates/sidebarlogo.html
index f220931..e42afdf 100644
--- a/docs/_templates/sidebarlogo.html
+++ b/docs/_templates/sidebarlogo.html
@@ -17,7 +17,7 @@ You are currently looking at the documentation of version {{ version }}.
<h3>Useful Links</h3>
<ul>
<li><a href="https://pypi.python.org/pypi/pyvisa/">PyVISA @ PyPI</a></li>
- <li><a href="https://github.com/hgrecco/pyvisa">Code in GitHub</a></li>
- <li><a href="https://github.com/hgrecco/pyvisa/issues">Issue Tracker</a></li>
+ <li><a href="https://github.com/pyvisa/pyvisa">Code in GitHub</a></li>
+ <li><a href="https://github.com/pyvisa/pyvisa/issues">Issue Tracker</a></li>
</ul>
diff --git a/docs/backends.rst b/docs/backends.rst
index 1aded42..53a7601 100644
--- a/docs/backends.rst
+++ b/docs/backends.rst
@@ -65,6 +65,13 @@ What does a minimum backend looks like? Quite simple::
Additionally you can provide a staticmethod named get_debug_info` that should return a
dictionary of debug information which is printed when you call ``python -m visa info``
+.. note::
+
+ Your backend name should not end by ``-script`` or it will be discarded.
+ This is because any script generated by setuptools containing the name
+ pyvisa will be named ``pyvisa-*-script`` and they are obviously not backends.
+ Examples are the ``pyvisa-shell`` and ``pyvisa-info`` scripts.
+
An important aspect of developing a backend is knowing which VisaLibraryBase method to
implement and what API to expose.
diff --git a/docs/conf.py b/docs/conf.py
index 1a1d32d..dd66e73 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -44,10 +44,10 @@ master_doc = 'index'
# General information about the project.
project = 'PyVISA'
author = 'PyVISA Authors'
+
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
-
version = pkg_resources.get_distribution(project).version
release = version
this_year = datetime.date.today().year
diff --git a/docs/configuring.rst b/docs/configuring.rst
index ddba398..867722e 100644
--- a/docs/configuring.rst
+++ b/docs/configuring.rst
@@ -1,5 +1,17 @@
.. _configuring:
+Configuring the backend
+============================
+Currently there are two backends available: The one included in pyvisa which
+uses the NI library. This is used by default and the configuration is described
+in the next chapter.
+And then there is pyvia-py a pure python implementation of the VISA libary.
+It can be selected by passing a parameter to the ResourceManager:
+
+ >>> visa.ResourceManager('@py')
+
+Alternativly it can also be selected by setting the environment variable
+PYVISA_LIBRARY. It takes the same values as the ResourceManager constructor.
Configuring the NI backend
==========================
@@ -58,4 +70,4 @@ solutions to common problem as well as useful debugging techniques. If everythin
feel free to open an issue in our `issue tracker`_
.. _`home directory`: http://en.wikipedia.org/wiki/Home_directory
-.. _`issue tracker`: https://github.com/hgrecco/pyvisa/issues
+.. _`issue tracker`: https://github.com/pyvisa/pyvisa/issues
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 0291f11..13a104a 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -25,15 +25,15 @@ To contribute fixes, code or documentation to PyVISA, send us a patch, or fork P
You can also get the code from PyPI_ or GitHub_. You can either clone the public repository::
- $ git clone git://github.com/hgrecco/pyvisa.git
+ $ git clone git://github.com/pyvisa/pyvisa.git
Download the tarball::
- $ curl -OL https://github.com/hgrecco/pyvisa/tarball/master
+ $ curl -OL https://github.com/pyvisa/pyvisa/tarball/master
Or, download the zipball::
- $ curl -OL https://github.com/hgrecco/pyvisa/zipball/master
+ $ curl -OL https://github.com/pyvisa/pyvisa/zipball/master
Once you have a copy of the source, you can embed it in your Python package, or install it into your site-packages easily::
@@ -70,7 +70,7 @@ it. You can use any of the existing backends as a template or start a thread in
.. _`Anaconda CE`: https://store.continuum.io/cshop/anaconda
.. _PyPI: https://pypi.python.org/pypi/PyVISA
.. _`National Instruments's VISA`: http://ni.com/visa/
-.. _github: http://github.com/hgrecco/pyvisa
-.. _`issue tracker`: https://github.com/hgrecco/pyvisa/issues
+.. _github: http://github.com/pyvisa/pyvisa
+.. _`issue tracker`: https://github.com/pyvisa/pyvisa/issues
diff --git a/docs/faq.rst b/docs/faq.rst
index b07f6d6..24df6df 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -169,6 +169,6 @@ Where can I get more information about VISA?
http://digital.ni.com/manuals.nsf/websearch/266526277DFF74F786256ADC0065C50C
-.. _`AUTHORS`: https://github.com/hgrecco/pyvisa/blob/master/AUTHORS
-.. _`Issue Tracker`: https://github.com/hgrecco/pyvisa/issues
+.. _`AUTHORS`: https://github.com/pyvisa/pyvisa/blob/master/AUTHORS
+.. _`Issue Tracker`: https://github.com/pyvisa/pyvisa/issues
.. _`virtual environment`: http://www.virtualenv.org/en/latest/
diff --git a/docs/getting.rst b/docs/getting.rst
index d6e85af..fda7fb7 100644
--- a/docs/getting.rst
+++ b/docs/getting.rst
@@ -3,7 +3,7 @@
Installation
============
-PyVISA is a frontend to the VISA library. It runs on Python 2.6+ and 3.2+.
+PyVISA is a frontend to the VISA library. It runs on Python 2.7 and 3.4+.
You can install it using pip_::
@@ -44,7 +44,7 @@ Using the development version
You can install the latest development version (at your own risk) directly form GitHub_::
- $ pip install -U https://github.com/hgrecco/pyvisa/zipball/master
+ $ pip install -U https://github.com/pyvisa/pyvisa/zipball/master
.. note:: If you have an old system installation of Python and you don't want to
@@ -57,6 +57,6 @@ You can install the latest development version (at your own risk) directly form
.. _pip: http://www.pip-installer.org/
.. _`Anaconda CE`: https://store.continuum.io/cshop/anaconda
.. _PyPI: https://pypi.python.org/pypi/PyVISA
-.. _GitHub: https://github.com/hgrecco/pyvisa
+.. _GitHub: https://github.com/pyvisa/pyvisa
.. _`National Instruments's VISA`: http://ni.com/visa/
-.. _`issue tracker`: https://github.com/hgrecco/pyvisa/issues
+.. _`issue tracker`: https://github.com/pyvisa/pyvisa/issues
diff --git a/docs/getting_nivisa.rst b/docs/getting_nivisa.rst
index 8a3c2e9..393f2eb 100644
--- a/docs/getting_nivisa.rst
+++ b/docs/getting_nivisa.rst
@@ -11,7 +11,7 @@ PyVISA includes a debugging command to help you troubleshoot this (and other thi
According to National Instruments, NI VISA **5.4.1** is available for:
-.. note:: NI-VISA is not available for your system, take a look at the :ref:`faq`.
+.. note:: If NI-VISA is not available for your system, take a look at the :ref:`faq`.
Mac OS X
diff --git a/docs/resources.rst b/docs/resources.rst
index 93a0211..031a65b 100644
--- a/docs/resources.rst
+++ b/docs/resources.rst
@@ -10,7 +10,7 @@ attributes an methods that are available by the underlying device.
You do not create this objects directly but they are returned by the
:meth:`pyvisa.highlevel.ResourceManager.open_resource` method of a :class:`pyvisa.highlevel.ResourceManager`. In general terms, there
-are two main groups derived from :class:`pyvisa.resources.Resource`, :class:`pyvisa.resources.RegisterBasedResource` and :class:`pyvisa.resources.RegisterBasedResource`.
+are two main groups derived from :class:`pyvisa.resources.Resource`, :class:`pyvisa.resources.RegisterBasedResource` and :class:`pyvisa.resources.MessageBasedResource`.
.. note:: The resource Python class to use is selected automatically from the
resource name. However, you can force a Resource Python class:
diff --git a/docs/rvalues.rst b/docs/rvalues.rst
index 383585c..aecb6a6 100644
--- a/docs/rvalues.rst
+++ b/docs/rvalues.rst
@@ -77,6 +77,11 @@ If you have doubles `d` in big endian the call will be::
You can also specify the output container type, just as it was shown before.
+By default, PyVISA will assume that the data block is formatted according to
+the IEEE convention. If your instrument uses HP data block you can pass
+``header_fmt='hp'`` to ``read_binary_values``. If your instrument does not use
+any header for the data simply ``header_fmt='empty'``.
+
Writing ASCII values
--------------------
@@ -178,4 +183,12 @@ In those cases, you need to get the data::
and then you need to implement the logic to parse it.
+Alternatively if the `read_raw` call fails you can try to read just a few bytes
+using::
+
+ >>> inst.write('CURV?')
+ >>> data = inst.read_bytes(1)
+If this call fails it may mean that your instrument did not answer, either
+because it needs more time or because your first instruction was not
+understood.
diff --git a/docs/shell.rst b/docs/shell.rst
index 0943d90..70d51e7 100644
--- a/docs/shell.rst
+++ b/docs/shell.rst
@@ -8,6 +8,10 @@ interface to interact with instruments. You can invoke it from the command-line:
python -m visa shell
+or::
+
+ pyvisa-shell
+
that will show something the following prompt::
Welcome to the VISA shell. Type help or ? to list commands.
@@ -20,7 +24,7 @@ At any time, you can type ``?`` or ``help`` to get a list of valid commands::
Documented commands (type help <topic>):
========================================
- EOF attr close exit help list open query read write
+ EOF attr close exit help list open query read timeout write
(visa) help list
List all connected resources.
@@ -45,6 +49,16 @@ Let's open comport 1::
(open) query *IDN?
Some Instrument, Some Company.
+You can print timeout that is set for query/read operation::
+
+ (open) timeout
+ Timeout: 2000ms
+
+Then also to change the timeout for example to 1500ms (1.5 sec)::
+
+ (open) timeout 1500
+ Done
+
We can also get a list of all visa attributes::
+-----------------------------+------------+----------------------------+-------------------------------------+
@@ -100,11 +114,97 @@ We can also get a list of all visa attributes::
| VI_ATTR_WR_BUF_SIZE | 1073676334 | | 4096 |
+-----------------------------+------------+----------------------------+-------------------------------------+
+
+To simplify the handling of VI_ATTR_TERMCHAR and VI_ATTR_TERMCHAR_EN, the command 'termchar' can be used.
+If only one character provided, it sets both read and write termination character to the same character.
+If two characters are provided, it sets read and write termination characters independently.
+
+To setup termchar to '\r' (CR or ascii code 10)::
+
+ (open) termchar CR
+ Done
+
+To read what termchar is defined::
+
+ (open) termchar
+ Termchar read: CR write: CR
+
+To setup read termchar to '\n' and write termchar to '\r\n\'::
+
+ (open) termchar LF CRLF
+ Done
+
+Supported termchar values are: CR ('\r'), LF ('\n'), CRLF ('\r\n') , NUL ('\0'), None.
+None is used to disable termchar.
+
+
Finally, you can close the device::
(open) close
+PyVisa Shell Backends
+=====================
+
+Based on available backend (see bellow for ``info`` command), it is possible to switch shell to use non-default backend via
+``-b BACKEND`` or ``--backend BACKEND``.
+
+You can invoke::
+
+ python -m visa -b sim shell
+
+or::
+
+ pyvisa-shell -b sim
+
+to use python-sim as backend instead of ni backend.
+This can be used for example for testing of python-sim configuration.
+
+You can invoke::
+
+ python -m visa -b py shell
+
+or::
+
+ pyvisa-shell -b py
+
+uses python-py as backend instead of ni backend, for situation when ni not installed.
+
+
+PyVisa Info
+===========
+
+You can invoke it from the command-line::
+
+ python -m visa info
+
+or::
+
+ pyvisa-info
+
+that will print information to diagnose PyVISA, info about Machine, Python, backends, etc ::
+
+ Machine Details:
+ Platform ID: Windows
+ Processor: Intel64 Family 6
+ ...
+ PyVISA Version: ...
+
+ Backends:
+ ni:
+ Version: 1.8 (bundled with PyVISA)
+ ...
+ py:
+ Version: 0.2
+ ...
+ sim:
+ Version: 0.3
+ Spec version: 1.1
+
+
+Summary
+=======
+
Cool, right? It will be great to have a GUI similar to NI-MAX, but we leave that to be developed outside PyVISA.
Want to help? Let us know!
diff --git a/pyvisa/compat/__init__.py b/pyvisa/compat/__init__.py
index a3dc8fb..abd0f78 100644
--- a/pyvisa/compat/__init__.py
+++ b/pyvisa/compat/__init__.py
@@ -9,9 +9,12 @@
:license: BSD, see LICENSE for more details.
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import sys
+
+
PYTHON3 = sys.version >= '3'
if PYTHON3:
@@ -23,6 +26,10 @@ if PYTHON3:
integer_types = (int, )
input = input
+
+ int_to_bytes = int.to_bytes
+ int_from_bytes = int.from_bytes
+
else:
string_types = basestring
@@ -35,14 +42,85 @@ else:
input = raw_input
-if sys.version_info < (2, 7):
- try:
- # noinspection PyPackageRequirements
- import unittest2 as unittest
- except ImportError:
- raise Exception("Testing PyVISA in Python 2.6 requires package 'unittest2'")
-else:
- import unittest
+ # The 2 following function implementation extracted from the python-future
+ # project
+ import collections
+
+ def int_to_bytes(integer, length, byteorder, signed=False):
+ """
+ Return an array of bytes representing an integer.
+ The integer is represented using length bytes. An OverflowError is
+ raised if the integer is not representable with the given number of
+ bytes.
+ The byteorder argument determines the byte order used to represent the
+ integer. If byteorder is 'big', the most significant byte is at the
+ beginning of the byte array. If byteorder is 'little', the most
+ significant byte is at the end of the byte array. To request the
+ native byte order of the host system, use `sys.byteorder' as the byte
+ order value.
+ The signed keyword-only argument determines whether two's complement is
+ used to represent the integer. If signed is False and a negative
+ integer is given, an OverflowError is raised.
+ """
+ if length < 0:
+ raise ValueError("length argument must be non-negative")
+ if length == 0 and integer == 0:
+ return bytes()
+ if signed and integer < 0:
+ bits = length * 8
+ num = (2**bits) + integer
+ if num <= 0:
+ raise OverflowError("int too smal to convert")
+ else:
+ if integer < 0:
+ raise OverflowError("can't convert negative int to unsigned")
+ num = integer
+ if byteorder not in ('little', 'big'):
+ raise ValueError("byteorder must be either 'little' or 'big'")
+ h = b'%x' % num
+ s = bytes((b'0'*(len(h) % 2) + h).zfill(length*2).decode('hex'))
+ if signed:
+ high_set = s[0] & 0x80
+ if integer > 0 and high_set:
+ raise OverflowError("int too big to convert")
+ if integer < 0 and not high_set:
+ raise OverflowError("int too small to convert")
+ if len(s) > length:
+ raise OverflowError("int too big to convert")
+ return s if byteorder == 'big' else s[::-1]
+
+ def int_from_bytes(mybytes, byteorder='big', signed=False):
+ """
+ Return the integer represented by the given array of bytes.
+ The mybytes argument must either support the buffer protocol or be an
+ iterable object producing bytes. Bytes and bytearray are examples of
+ built-in objects that support the buffer protocol.
+ The byteorder argument determines the byte order used to represent the
+ integer. If byteorder is 'big', the most significant byte is at the
+ beginning of the byte array. If byteorder is 'little', the most
+ significant byte is at the end of the byte array. To request the
+ native byte order of the host system, use `sys.byteorder' as the byte
+ order value.
+ The signed keyword-only argument indicates whether two's complement is
+ used to represent the integer.
+ """
+ if byteorder not in ('little', 'big'):
+ raise ValueError("byteorder must be either 'little' or 'big'")
+ if isinstance(mybytes, unicode):
+ raise TypeError("cannot convert unicode objects to bytes")
+ # mybytes can also be passed as a sequence of integers on Py3.
+ # Test for this:
+ elif isinstance(mybytes, collections.Iterable):
+ mybytes = bytes(mybytes)
+ b = mybytes if byteorder == 'big' else mybytes[::-1]
+ if len(b) == 0:
+ b = b'\x00'
+ # The encode() method has been disabled by newbytes, but Py2's
+ # str has it:
+ num = int(b.encode('hex'), 16)
+ if signed and (b[0] & 0x80):
+ num = num - (2 ** (len(b)*8))
+ return num
try:
from collections import OrderedDict
@@ -54,11 +132,6 @@ try:
except ImportError:
from .nullhandler import NullHandler
-try:
- from subprocess import check_output
-except ImportError:
- from .check_output import check_output
-
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
diff --git a/pyvisa/compat/check_output.py b/pyvisa/compat/check_output.py
deleted file mode 100644
index 4d57e8b..0000000
--- a/pyvisa/compat/check_output.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- pyvisa.compat.check_output
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- Taken from the Python 2.7 source code.
-
- :copyright: 2013, PSF
- :license: PSF License
-"""
-
-from __future__ import division, unicode_literals, print_function, absolute_import
-
-import subprocess
-
-
-def check_output(*popenargs, **kwargs):
- r"""Run command with arguments and return its output as a byte string.
-
- Backported from Python 2.7 as it's implemented as pure python on stdlib.
-
- >>> check_output(['/usr/bin/python', '--version'])
- Python 2.6.2
- """
- process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
- output, unused_err = process.communicate()
- retcode = process.poll()
- if retcode:
- cmd = kwargs.get("args")
- if cmd is None:
- cmd = popenargs[0]
- error = subprocess.CalledProcessError(retcode, cmd)
- error.output = output
- raise error
- return output
diff --git a/pyvisa/compat/struct.py b/pyvisa/compat/struct.py
index 154ddbf..2d0e429 100644
--- a/pyvisa/compat/struct.py
+++ b/pyvisa/compat/struct.py
@@ -9,7 +9,8 @@
:license: PSF License
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import sys
import struct
diff --git a/pyvisa/constants.py b/pyvisa/constants.py
index 2172306..c2e907f 100644
--- a/pyvisa/constants.py
+++ b/pyvisa/constants.py
@@ -688,7 +688,7 @@ class InterfaceType(enum.IntEnum):
class AddressState(enum.IntEnum):
- unaddressed =VI_GPIB_UNADDRESSED
+ unaddressed = VI_GPIB_UNADDRESSED
talker = VI_GPIB_TALKER
listenr = VI_GPIB_LISTENER
diff --git a/pyvisa/ctwrapper/functions.py b/pyvisa/ctwrapper/functions.py
index 8208067..472d573 100644
--- a/pyvisa/ctwrapper/functions.py
+++ b/pyvisa/ctwrapper/functions.py
@@ -40,7 +40,9 @@ visa_functions = [
"terminate", "uninstall_handler", "unlock", "unmap_address",
"unmap_trigger", "usb_control_in", "usb_control_out",
"vxi_command_query", "wait_on_event",
- "write", "write_asynchronously", "write_from_file"]
+ "write", "write_asynchronously", "write_from_file",
+ "in_64", "move_in_64", "out_64", "move_out_64", "poke_64",
+ "peek_64"]
__all__ = ["visa_functions", 'set_signatures'] + visa_functions
diff --git a/pyvisa/ctwrapper/highlevel.py b/pyvisa/ctwrapper/highlevel.py
index 1399046..8269c41 100644
--- a/pyvisa/ctwrapper/highlevel.py
+++ b/pyvisa/ctwrapper/highlevel.py
@@ -72,10 +72,10 @@ class NIVisaLibrary(highlevel.VisaLibraryBase):
user_lib = read_user_library_path()
tmp = [find_library(library_path)
- for library_path in ('visa', 'visa32', 'visa32.dll')]
+ for library_path in ('visa', 'visa32', 'visa32.dll', 'visa64', 'visa64.dll')]
tmp = [LibraryPath(library_path)
- for library_path in tmp
+ for library_path in set(tmp)
if library_path is not None]
logger.debug('Automatically found library files: %s' % tmp)
diff --git a/pyvisa/errors.py b/pyvisa/errors.py
index 94ec7d1..b29534a 100644
--- a/pyvisa/errors.py
+++ b/pyvisa/errors.py
@@ -351,6 +351,9 @@ class Error(Exception):
def __init__(self, description):
super(Error, self).__init__(description)
+
+ def __eq__(self, other):
+ return self.__dict__ == other.__dict__
class VisaIOError(Error):
@@ -368,6 +371,9 @@ class VisaIOError(Error):
self.error_code = error_code
self.abbreviation = abbreviation
self.description = description
+
+ def __reduce__(self):
+ return (VisaIOError, (self.error_code,))
class VisaIOWarning(Warning):
@@ -384,6 +390,12 @@ class VisaIOWarning(Warning):
self.error_code = error_code
self.abbreviation = abbreviation
self.description = description
+
+ def __reduce__(self):
+ return (VisaIOWarning, (self.error_code,))
+
+ def __eq__(self, other):
+ return self.__dict__ == other.__dict__
class VisaTypeError(Error):
@@ -410,12 +422,22 @@ class UnknownHandler(Error):
def __init__(self, event_type, handler, user_handle):
super(UnknownHandler, self).__init__('%s, %s, %s' % (event_type, handler, user_handle))
+ self.event_type = event_type
+ self.handler = handler
+ self.user_handle = user_handle
+
+ def __reduce__(self):
+ return (UnknownHandler, (self.event_type, self.handler, self.user_handle))
class OSNotSupported(Error):
def __init__(self, os):
super(OSNotSupported, self).__init__(os + " is not yet supported by PyVISA")
+ self.os = os
+
+ def __reduce__(self):
+ return (OSNotSupported, (self.os,))
class InvalidBinaryFormat(Error):
@@ -424,6 +446,10 @@ class InvalidBinaryFormat(Error):
if description:
description = ": " + description
super(InvalidBinaryFormat, self).__init__("Unrecognized binary data format" + description)
+ self.description = description
+
+ def __reduce__(self):
+ return (InvalidBinaryFormat, (self.description,))
class InvalidSession(Error):
@@ -433,6 +459,9 @@ class InvalidSession(Error):
def __init__(self):
super(InvalidSession, self).__init__('Invalid session handle. The resource might be closed.')
+ def __reduce__(self):
+ return (InvalidSession, ())
+
class LibraryError(OSError, Error):
diff --git a/pyvisa/highlevel.py b/pyvisa/highlevel.py
index a07f34a..b6f610b 100644
--- a/pyvisa/highlevel.py
+++ b/pyvisa/highlevel.py
@@ -16,6 +16,7 @@ from __future__ import division, unicode_literals, print_function, absolute_impo
import contextlib
import collections
import pkgutil
+import os
from collections import defaultdict
from . import logger
@@ -246,7 +247,7 @@ class VisaLibraryBase(object):
raise ValueError('%s is not a valid size. Valid values are 8, 16, 32 or 64' % width)
def write_memory(self, session, space, offset, data, width, extended=False):
- """Write in an 8-bit, 16-bit, 32-bit, value to the specified memory space and offset.
+ """Write in an 8-bit, 16-bit, 32-bit, 64-bit value to the specified memory space and offset.
Corresponds to viOut* functions of the VISA library.
@@ -265,8 +266,10 @@ class VisaLibraryBase(object):
return self.out_16(session, space, offset, data, extended)
elif width == 32:
return self.out_32(session, space, offset, data, extended)
+ elif width == 64:
+ return self.out_64(session, space, offset, data, extended)
- raise ValueError('%s is not a valid size. Valid values are 8, 16 or 32' % width)
+ raise ValueError('%s is not a valid size. Valid values are 8, 16, 32, or 64' % width)
def move_in(self, session, space, offset, length, width, extended=False):
"""Moves a block of data to local memory from the specified address space and offset.
@@ -322,7 +325,7 @@ class VisaLibraryBase(object):
raise ValueError('%s is not a valid size. Valid values are 8, 16, 32 or 64' % width)
def peek(self, session, address, width):
- """Read an 8, 16 or 32-bit value from the specified address.
+ """Read an 8, 16, 32, or 64-bit value from the specified address.
Corresponds to viPeek* functions of the VISA library.
@@ -345,7 +348,7 @@ class VisaLibraryBase(object):
raise ValueError('%s is not a valid size. Valid values are 8, 16, 32 or 64' % width)
def poke(self, session, address, width, data):
- """Writes an 8, 16 or 32-bit value from the specified address.
+ """Writes an 8, 16, 32, or 64-bit value from the specified address.
Corresponds to viPoke* functions of the VISA library.
@@ -363,8 +366,10 @@ class VisaLibraryBase(object):
return self.poke_16(session, address, data)
elif width == 32:
return self.poke_32(session, address, data)
+ elif width == 64:
+ return self.poke_64(session, address, data)
- raise ValueError('%s is not a valid size. Valid values are 8, 16 or 32' % width)
+ raise ValueError('%s is not a valid size. Valid values are 8, 16, 32, or 64' % width)
# Methods that VISA Library implementations must implement
@@ -1035,7 +1040,8 @@ class VisaLibraryBase(object):
return (ResourceInfo(parsed.interface_type_const,
parsed.board,
- parsed.resource_class, None, None),
+ parsed.resource_class,
+ str(parsed), None),
constants.StatusCode.success)
except ValueError:
return 0, constants.StatusCode.error_invalid_resource_name
@@ -1411,7 +1417,7 @@ def list_backends():
:rtype: list
"""
return ['ni'] + [name for (loader, name, ispkg) in pkgutil.iter_modules()
- if name.startswith('pyvisa-')]
+ if name.startswith('pyvisa-') and not name.endswith('-script')]
#: Maps backend name to VisaLibraryBase derived class
@@ -1432,12 +1438,11 @@ def get_wrapper_class(backend_name):
_WRAPPERS['ni'] = NIVisaLibrary
return NIVisaLibrary
- for pkgname in list_backends():
- if pkgname.endswith('-' + backend_name):
- pkg = __import__(pkgname)
- _WRAPPERS[backend_name] = cls = pkg.WRAPPER_CLASS
- return cls
- else:
+ try:
+ pkg = __import__('pyvisa-' + backend_name)
+ _WRAPPERS[backend_name] = cls = pkg.WRAPPER_CLASS
+ return cls
+ except ImportError:
raise ValueError('Wrapper not found: No package named pyvisa-%s' % backend_name)
@@ -1448,6 +1453,12 @@ def open_visa_library(specification):
wrapper will be created automatically when you create a ResourceManager object.
"""
+ if not specification:
+ try:
+ specification = os.environ['PYVISA_LIBRARY']
+ except KeyError:
+ logger.debug('No visa libaray specified and environment variable PYVISA_LIBRARY is unset. Using NI library')
+
try:
argument, wrapper = specification.split('@')
except ValueError:
diff --git a/pyvisa/resources/messagebased.py b/pyvisa/resources/messagebased.py
index 2bf22e6..588ed26 100644
--- a/pyvisa/resources/messagebased.py
+++ b/pyvisa/resources/messagebased.py
@@ -11,7 +11,8 @@
:license: MIT, see LICENSE for more details.
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import contextlib
import time
@@ -47,7 +48,8 @@ class ValuesFormat(object):
self.container = container
self.is_binary = False
- def use_binary(self, datatype, is_big_endian, container=list, header_fmt='ieee'):
+ def use_binary(self, datatype, is_big_endian, container=list,
+ header_fmt='ieee'):
self.datatype = datatype
self.is_big_endian = is_big_endian
self.container = container
@@ -56,19 +58,21 @@ class ValuesFormat(object):
class ControlRenMixin(object):
- """Common controlt_ren method of some messaged based resources.
+ """Common control_ren method of some messaged based resources.
+
"""
# It should work for GPIB, USB and some TCPIP
- # For TCPIP I found some (all?) NI's VISA library do not handle control_ren, but
- # it works for Agilent's VISA library (at least some of them)
+ # For TCPIP I found some (all?) NI's VISA library do not handle
+ # control_ren, but it works for Agilent's VISA library (at least some of
+ # them)
def control_ren(self, mode):
- """Controls the state of the GPIB Remote Enable (REN) interface line, and optionally the remote/local
- state of the device.
+ """Controls the state of the GPIB Remote Enable (REN) interface line,
+ and optionally the remote/local state of the device.
Corresponds to viGpibControlREN function of the VISA library.
- :param mode: Specifies the state of the REN line and optionally the device remote/local state.
- (Constants.GPIB_REN*)
+ :param mode: Specifies the state of the REN line and optionally the
+ device remote/local state. (Constants.GPIB_REN*)
:return: return value of the library call.
:rtype: VISAStatus
"""
@@ -95,17 +99,21 @@ class MessageBasedResource(Resource):
self._values_format = ValuesFormat()
super(MessageBasedResource, self).__init__(*args, **kwargs)
- # This is done for backwards compatibility but will be removed in 1.7
+ # This is done for backwards compatibility but will be removed in 1.10
@property
def values_format(self):
+ warnings.warn('values_format is deprecated and will be removed in '
+ '1.10', FutureWarning)
return self._values_format
@values_format.setter
def values_format(self, fmt):
+ warnings.warn('values_format is deprecated and will be removed in '
+ '1.10', FutureWarning)
self._values_format.is_binary = not (fmt & 0x01 == 0)
- if fmt & 0x03 == 1: #single
+ if fmt & 0x03 == 1: # single
self._values_format.datatype = 'f'
- elif fmt & 0x03 == 3: #double:
+ elif fmt & 0x03 == 3: # double:
self._values_format.datatype = 'd'
else:
raise ValueError("unknown data values fmt requested")
@@ -118,10 +126,14 @@ class MessageBasedResource(Resource):
def ask_delay(self):
"""An alias for query_delay kept for backwards compatibility.
"""
+ warnings.warn('ask_delay is deprecated and will be removed in '
+ '1.10, use query_delay instead', FutureWarning)
return self.query_delay
@ask_delay.setter
def ask_delay(self, value):
+ warnings.warn('ask_delay is deprecated and will be removed in '
+ '1.10, use query_delay instead', FutureWarning)
self.query_delay = value
@property
@@ -132,7 +144,8 @@ class MessageBasedResource(Resource):
@encoding.setter
def encoding(self, encoding):
- _ = 'test encoding'.encode(encoding).decode(encoding)
+ # Test that the encoding specified makes sense.
+ 'test encoding'.encode(encoding).decode(encoding)
self._encoding = encoding
@property
@@ -145,22 +158,25 @@ class MessageBasedResource(Resource):
def read_termination(self, value):
if value:
- # termination character, the rest is just used for verification after
- # each read operation.
+ # termination character, the rest is just used for verification
+ # after each read operation.
last_char = value[-1:]
- # Consequently, it's illogical to have the real termination character
- # twice in the sequence (otherwise reading would stop prematurely).
+ # Consequently, it's illogical to have the real termination
+ # character twice in the sequence (otherwise reading would stop
+ # prematurely).
if last_char in value[:-1]:
raise ValueError("ambiguous ending in termination characters")
self.set_visa_attribute(constants.VI_ATTR_TERMCHAR, ord(last_char))
- self.set_visa_attribute(constants.VI_ATTR_TERMCHAR_EN, constants.VI_TRUE)
+ self.set_visa_attribute(constants.VI_ATTR_TERMCHAR_EN,
+ constants.VI_TRUE)
else:
- # The termchar is also used in VI_ATTR_ASRL_END_IN (for serial termination)
- # so return it to its default.
+ # The termchar is also used in VI_ATTR_ASRL_END_IN (for serial
+ # termination) so return it to its default.
self.set_visa_attribute(constants.VI_ATTR_TERMCHAR, ord(self.LF))
- self.set_visa_attribute(constants.VI_ATTR_TERMCHAR_EN, constants.VI_FALSE)
+ self.set_visa_attribute(constants.VI_ATTR_TERMCHAR_EN,
+ constants.VI_FALSE)
self._read_termination = value
@@ -208,8 +224,10 @@ class MessageBasedResource(Resource):
return count
- def write_ascii_values(self, message, values, converter='f', separator=',', termination=None, encoding=None):
- """Write a string message to the device followed by values in ascii format.
+ def write_ascii_values(self, message, values, converter='f', separator=',',
+ termination=None, encoding=None):
+ """Write a string message to the device followed by values in ascii
+ format.
The write_termination is always appended to it.
@@ -220,8 +238,8 @@ class MessageBasedResource(Resource):
String formatting codes are also accepted.
Defaults to str.
:type converter: callable | str
- :param separator: a callable that split the str into individual elements.
- If a str is given, data.split(separator) is used.
+ :param separator: a callable that join the values in a single str.
+ If a str is given, separator.join(values) is used.
:type: separator: (collections.Iterable[T]) -> str | str
:return: number of bytes written.
:rtype: int
@@ -236,7 +254,7 @@ class MessageBasedResource(Resource):
block = util.to_ascii_block(values, converter, separator)
- message = message.encode(enco) + block
+ message = message.encode(enco) + block.encode(enco)
if term:
message += term.encode(enco)
@@ -245,15 +263,19 @@ class MessageBasedResource(Resource):
return count
- def write_binary_values(self, message, values, datatype='f', is_big_endian=False, termination=None, encoding=None):
- """Write a string message to the device followed by values in binary format.
+ def write_binary_values(self, message, values, datatype='f',
+ is_big_endian=False, termination=None,
+ encoding=None):
+ """Write a string message to the device followed by values in binary
+ format.
The write_termination is always appended to it.
:param message: the message to be sent.
:type message: unicode (Py2) or str (Py3)
:param values: data to be writen to the device.
- :param datatype: the format string for a single element. See struct module.
+ :param datatype: the format string for a single element. See struct
+ module.
:param is_big_endian: boolean indicating endianess.
:return: number of bytes written.
:rtype: int
@@ -277,36 +299,95 @@ class MessageBasedResource(Resource):
return count
def write_values(self, message, values, termination=None, encoding=None):
-
+ warnings.warn('write_values is deprecated and will be removed in '
+ '1.10, use write_ascii_values or write_binary_values '
+ 'instead.', FutureWarning)
vf = self.values_format
if vf.is_binary:
- return self.write_binary_values(message, values, vf.datatype, vf.is_big_endian, termination, encoding)
+ return self.write_binary_values(message, values, vf.datatype,
+ vf.is_big_endian, termination,
+ encoding)
- return self.write_ascii_values(message, values, vf.converter, vf.separator, termination, encoding)
+ return self.write_ascii_values(message, values, vf.converter,
+ vf.separator, termination, encoding)
+
+ def read_bytes(self, count, chunk_size=None, break_on_termchar=False):
+ """Read a certain number of bytes from the instrument.
+
+ :param count: The number of bytes to read from the instrument.
+ :type count: int
+ :param chunk_size: The chunk size to use to perform the reading.
+ :type chunk_size: int
+ :param break_on_termchar: Should the reading stop when a termination
+ character is encountered.
+ :type brak_on_termchar: bool
+
+ :rtype: bytes
+
+ """
+ chunk_size = chunk_size or self.chunk_size
+ ret = bytearray()
+ left_to_read = count
+ termchar_read = constants.StatusCode.success_termination_character_read
+
+ with self.ignore_warning(constants.VI_SUCCESS_DEV_NPRESENT,
+ constants.VI_SUCCESS_MAX_CNT):
+ try:
+ status = None
+ while len(ret) < count:
+ size = min(chunk_size, left_to_read)
+ logger.debug('%s - reading %d bytes (last status %r)',
+ self._resource_name, size, status)
+ chunk, status = self.visalib.read(self.session, size)
+ ret.extend(chunk)
+ left_to_read -= len(chunk)
+ if break_on_termchar and status == termchar_read:
+ break
+ except errors.VisaIOError as e:
+ logger.debug('%s - exception while reading: %s\n'
+ 'Buffer content: %r', self._resource_name, e,
+ ret)
+ raise
+ return bytes(ret)
def read_raw(self, size=None):
"""Read the unmodified string sent from the instrument to the computer.
In contrast to read(), no termination characters are stripped.
+ :param size: The chunk size to use when reading the data.
+
:rtype: bytes
"""
+ return bytes(self._read_raw(size))
+
+ def _read_raw(self, size=None):
+ """Read the unmodified string sent from the instrument to the computer.
+
+ In contrast to read(), no termination characters are stripped.
+
+ :param size: The chunk size to use when reading the data.
+
+ :rtype: bytearray
+ """
size = self.chunk_size if size is None else size
loop_status = constants.StatusCode.success_max_count_read
- ret = bytes()
- with self.ignore_warning(constants.VI_SUCCESS_DEV_NPRESENT, constants.VI_SUCCESS_MAX_CNT):
+ ret = bytearray()
+ with self.ignore_warning(constants.VI_SUCCESS_DEV_NPRESENT,
+ constants.VI_SUCCESS_MAX_CNT):
try:
status = loop_status
while status == loop_status:
logger.debug('%s - reading %d bytes (last status %r)',
self._resource_name, size, status)
chunk, status = self.visalib.read(self.session, size)
- ret += chunk
+ ret.extend(chunk)
except errors.VisaIOError as e:
- logger.debug('%s - exception while reading: %s', self._resource_name, e)
+ logger.debug('%s - exception while reading: %s\nBuffer '
+ 'content: %r', self._resource_name, e, ret)
raise
return ret
@@ -329,10 +410,10 @@ class MessageBasedResource(Resource):
if termination is None:
termination = self._read_termination
- message = self.read_raw().decode(enco)
+ message = self._read_raw().decode(enco)
else:
with self.read_termination_context(termination):
- message = self.read_raw().decode(enco)
+ message = self._read_raw().decode(enco)
if not termination:
return message
@@ -343,6 +424,78 @@ class MessageBasedResource(Resource):
return message[:-len(termination)]
+ def read_ascii_values(self, converter='f', separator=',', container=list,
+ delay=None):
+ """Read values from the device in ascii format returning an iterable of
+ values.
+
+ :param delay: delay in seconds between write and read operations.
+ if None, defaults to self.query_delay
+ :param converter: function used to convert each element.
+ Defaults to float
+ :type converter: callable
+ :param separator: a callable that split the str into individual
+ elements. If a str is given, data.split(separator) is
+ used.
+ :type: separator: (str) -> collections.Iterable[int] | str
+ :param container: container type to use for the output data.
+ :returns: the answer from the device.
+ :rtype: list
+
+ """
+ # Use read rather than _read_raw because we cannot handle a bytearray
+ block = self.read()
+
+ return util.from_ascii_block(block, converter, separator, container)
+
+ def read_binary_values(self, datatype='f', is_big_endian=False,
+ container=list, header_fmt='ieee'):
+ """Read values from the device in binary format returning an iterable
+ of values.
+
+ :param datatype: the format string for a single element. See struct
+ module.
+ :param is_big_endian: boolean indicating endianess.
+ Defaults to False.
+ :param container: container type to use for the output data.
+ :param header_fmt: format of the header prefixing the data. Possible
+ values are: 'ieee', 'hp', 'empty'
+ :returns: the answer from the device.
+ :rtype: type(container)
+
+ """
+ block = self._read_raw()
+ expected_length = 0
+
+ if header_fmt == 'ieee':
+ offset, data_length = util.parse_ieee_block_header(block)
+ expected_length = offset + data_length
+ elif header_fmt == 'hp':
+ offset, data_length = util.parse_hp_block_header(block,
+ is_big_endian)
+ expected_length = offset + data_length
+
+ if self._read_termination is not None:
+ expected_length += len(self._read_termination)
+
+ while len(block) < expected_length:
+ block.extend(self._read_raw())
+
+ try:
+ if header_fmt == 'ieee':
+ return util.from_ieee_block(block, datatype, is_big_endian,
+ container)
+ elif header_fmt == 'hp':
+ return util.from_hp_block(block, datatype, is_big_endian,
+ container)
+ elif header_fmt == 'empty':
+ return util.from_binary_block(block, 0, None, datatype,
+ is_big_endian, container)
+ else:
+ raise
+ except ValueError as e:
+ raise errors.InvalidBinaryFormat(e.args)
+
def read_values(self, fmt=None, container=list):
"""Read a list of floating point values from the device.
@@ -355,13 +508,17 @@ class MessageBasedResource(Resource):
:return: the list of read values
:rtype: list
"""
+ warnings.warn('read_values is deprecated and will be removed in '
+ '1.10, use read_ascii_values or read_binary_values '
+ 'instead.', FutureWarning)
if not fmt:
vf = self.values_format
if not vf.is_binary:
return util.from_ascii_block(self.read(), container)
- data = self.read_raw()
+ data = self._read_raw()
try:
- return util.parse_binary(data, vf.is_big_endian, vf.datatype=='f')
+ return util.parse_binary(data, vf.is_big_endian,
+ vf.datatype == 'f')
except ValueError as e:
try:
msg = e.args[0]
@@ -369,20 +526,20 @@ class MessageBasedResource(Resource):
msg = ''
raise errors.InvalidBinaryFormat(msg)
- if fmt & 0x01 == 0: # ascii
+ if fmt & 0x01 == 0: # ascii
return util.from_ascii_block(self.read())
- data = self.read_raw()
+ data = self._read_raw()
try:
- if fmt & 0x03 == 1: #single
+ if fmt & 0x03 == 1: # single
is_single = True
- elif fmt & 0x03 == 3: #double:
+ elif fmt & 0x03 == 3: # double:
is_single = False
else:
raise ValueError("unknown data values fmt requested")
- is_big_endian = fmt & 0x04 # big endian
+ is_big_endian = fmt & 0x04 # big endian
return util.parse_binary(data, is_big_endian, is_single)
except ValueError as e:
raise errors.InvalidBinaryFormat(e.args)
@@ -406,8 +563,10 @@ class MessageBasedResource(Resource):
time.sleep(delay)
return self.read()
- # Kept for backwards compatibility.
- ask = query
+ def ask(self, message, delay=None):
+ warnings.warn('ask is deprecated and will be removed in '
+ '1.10, use query instead.', FutureWarning)
+ return self.query(message, delay)
def query_values(self, message, delay=None):
"""Query the device for values returning an iterable of values.
@@ -421,15 +580,23 @@ class MessageBasedResource(Resource):
:returns: the answer from the device.
:rtype: list
"""
+ warnings.warn('query_values is deprecated and will be removed in '
+ '1.10, use query_ascii_values or quey_binary_values '
+ 'instead.', FutureWarning)
vf = self.values_format
if vf.is_binary:
- return self.query_binary_values(message, vf.datatype, vf.is_big_endian, vf.container, delay, vf.header_fmt)
+ return self.query_binary_values(message, vf.datatype,
+ vf.is_big_endian, vf.container,
+ delay, vf.header_fmt)
- return self.query_ascii_values(message, vf.converter, vf.separator, vf.container, delay)
+ return self.query_ascii_values(message, vf.converter, vf.separator,
+ vf.container, delay)
- def query_ascii_values(self, message, converter='f', separator=',', container=list, delay=None):
- """Query the device for values in ascii format returning an iterable of values.
+ def query_ascii_values(self, message, converter='f', separator=',',
+ container=list, delay=None):
+ """Query the device for values in ascii format returning an iterable of
+ values.
:param message: the message to send.
:type message: str
@@ -438,8 +605,9 @@ class MessageBasedResource(Resource):
:param converter: function used to convert each element.
Defaults to float
:type converter: callable
- :param separator: a callable that split the str into individual elements.
- If a str is given, data.split(separator) is used.
+ :param separator: a callable that split the str into individual
+ elements. If a str is given, data.split(separator) is
+ used.
:type: separator: (str) -> collections.Iterable[int] | str
:param container: container type to use for the output data.
:returns: the answer from the device.
@@ -452,25 +620,28 @@ class MessageBasedResource(Resource):
if delay > 0.0:
time.sleep(delay)
- block = self.read()
-
- return util.from_ascii_block(block, converter, separator, container)
+ return self.read_ascii_values(converter, separator, container,
+ delay)
- def query_binary_values(self, message, datatype='f', is_big_endian=False, container=list, delay=None, header_fmt='ieee'):
- """Converts an iterable of numbers into a block of data in the ieee format.
+ def query_binary_values(self, message, datatype='f', is_big_endian=False,
+ container=list, delay=None, header_fmt='ieee'):
+ """Query the device for values in binary format returning an iterable
+ of values.
:param message: the message to send to the instrument.
- :param datatype: the format string for a single element. See struct module.
+ :param datatype: the format string for a single element. See struct
+ module.
:param is_big_endian: boolean indicating endianess.
Defaults to False.
:param container: container type to use for the output data.
:param delay: delay in seconds between write and read operations.
if None, defaults to self.query_delay
- :rtype: bytes
+ :returns: the answer from the device.
+ :rtype: list
"""
-
if header_fmt not in ('ieee', 'empty', 'hp'):
- raise ValueError("Invalid header format. Valid options are 'ieee', 'empty', 'hp'")
+ raise ValueError("Invalid header format. Valid options are 'ieee',"
+ " 'empty', 'hp'")
self.write(message)
if delay is None:
@@ -478,24 +649,8 @@ class MessageBasedResource(Resource):
if delay > 0.0:
time.sleep(delay)
- block = self.read_raw()
-
- if header_fmt == 'ieee':
- offset, data_length = util.parse_ieee_block_header(block)
- expected_length = offset + data_length
-
- while len(block) < expected_length:
- block += self.read_raw()
-
- try:
- if header_fmt == 'ieee':
- return util.from_ieee_block(block, datatype, is_big_endian, container)
- elif header_fmt == 'empty':
- return util.from_binary_block(block, 0, None, datatype, is_big_endian, container)
- elif header_fmt == 'hp':
- return util.from_binary_block(block, 4, None, datatype, is_big_endian, container)
- except ValueError as e:
- raise errors.InvalidBinaryFormat(e.args)
+ return self.read_binary_values(datatype, is_big_endian, container,
+ header_fmt)
def ask_for_values(self, message, fmt=None, delay=None):
"""A combination of write(message) and read_values()
@@ -507,7 +662,9 @@ class MessageBasedResource(Resource):
:returns: the answer from the device.
:rtype: list
"""
-
+ warnings.warn('ask_values is deprecated and will be removed in '
+ '1.10, use query_ascii_values or quey_binary_values '
+ 'instead.', FutureWarning)
self.write(message)
if delay is None:
delay = self.query_delay
@@ -519,7 +676,8 @@ class MessageBasedResource(Resource):
"""Sends a software trigger to the device.
"""
- self.visalib.assert_trigger(self.session, constants.VI_TRIG_PROT_DEFAULT)
+ self.visalib.assert_trigger(self.session,
+ constants.VI_TRIG_PROT_DEFAULT)
@property
def stb(self):
@@ -536,10 +694,12 @@ class MessageBasedResource(Resource):
@contextlib.contextmanager
def read_termination_context(self, new_termination):
term = self.get_visa_attribute(constants.VI_ATTR_TERMCHAR)
- self.set_visa_attribute(constants.VI_ATTR_TERMCHAR, ord(new_termination[-1]))
+ self.set_visa_attribute(constants.VI_ATTR_TERMCHAR,
+ ord(new_termination[-1]))
yield
self.set_visa_attribute(constants.VI_ATTR_TERMCHAR, term)
# Rohde and Schwarz Device via Passport. Not sure which Resource should be.
-MessageBasedResource.register(constants.InterfaceType.rsnrp, 'INSTR')(MessageBasedResource)
+MessageBasedResource.register(constants.InterfaceType.rsnrp,
+ 'INSTR')(MessageBasedResource)
diff --git a/pyvisa/resources/resource.py b/pyvisa/resources/resource.py
index 4d33e91..46c4963 100644
--- a/pyvisa/resources/resource.py
+++ b/pyvisa/resources/resource.py
@@ -113,6 +113,12 @@ class Resource(object):
def __repr__(self):
return "<%r(%r)>" % (self.__class__.__name__, self.resource_name)
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
@property
def last_status(self):
"""Last status code for this session.
@@ -261,8 +267,10 @@ class Resource(object):
:param name: Attribute for which the state is to be modified. (Attributes.*)
:param state: The state of the attribute to be set for the specified object.
+ :return: return value of the library call.
+ :rtype: :class:`pyvisa.constants.StatusCode`
"""
- self.visalib.set_attribute(self.session, name, state)
+ return self.visalib.set_attribute(self.session, name, state)
def clear(self):
"""Clears this resource
@@ -334,7 +342,7 @@ class Resource(object):
event_type, context, ret = self.visalib.wait_on_event(self.session, in_event_type, timeout)
except errors.VisaIOError as exc:
if capture_timeout and exc.error_code == constants.StatusCode.error_timeout:
- return WaitResponse(0, None, exc.error_code, self.visalib, timed_out=True)
+ return WaitResponse(in_event_type, None, exc.error_code, self.visalib, timed_out=True)
raise
return WaitResponse(event_type, context, ret, self.visalib)
diff --git a/pyvisa/shell.py b/pyvisa/shell.py
index ef2175d..755b49b 100644
--- a/pyvisa/shell.py
+++ b/pyvisa/shell.py
@@ -18,7 +18,7 @@ import cmd
import sys
from .compat import input
-from . import ResourceManager, constants, VisaIOError
+from . import ResourceManager, attributes, constants, VisaIOError
from .thirdparty import prettytable
if sys.platform == 'darwin':
@@ -56,7 +56,7 @@ if sys.platform == 'darwin':
if intro is not None:
self.intro = intro
if self.intro:
- self.stdout.write(str(self.intro)+"\n")
+ self.stdout.write(str(self.intro) + "\n")
stop = None
while not stop:
if self.cmdqueue:
@@ -129,7 +129,7 @@ class VisaShell(Cmd):
self.resources = []
for ndx, (resource_name, value) in enumerate(resources.items()):
if not args:
- print('({:2d}) {}'.format(ndx, resource_name))
+ print('({0:2d}) {1}'.format(ndx, resource_name))
if value.alias:
print(' alias: {}'.format(value.alias))
@@ -156,8 +156,8 @@ class VisaShell(Cmd):
try:
self.current = self.resource_manager.open_resource(args)
print('{} has been opened.\n'
- 'You can talk to the device using "write", "read" or "query.\n'
- 'The default end of message is added to each message'.format(args))
+ 'You can talk to the device using "write", "read" or "query".\n'
+ 'The default end of message is added to each message.'.format(args))
self.py_attr = []
self.vi_attr = []
@@ -228,6 +228,38 @@ class VisaShell(Cmd):
except Exception as e:
print(e)
+ def do_timeout(self, args):
+ """Get or set timeout (in ms) for resource in use.
+
+ Get timeout:
+
+ timeout
+
+ Set timeout:
+
+ timeout <mstimeout>
+
+ """
+
+ if not self.current:
+ print('There are no resources in use. Use the command "open".')
+ return
+
+ args = args.strip()
+
+ if not args:
+ try:
+ print('Timeout: {}ms'.format(self.current.timeout))
+ except Exception as e:
+ print(e)
+ else:
+ args = args.split(' ')
+ try:
+ self.current.timeout = float(args[0])
+ print('Done')
+ except Exception as e:
+ print(e)
+
def print_attribute_list(self):
p = prettytable.PrettyTable(('VISA name', 'Constant', 'Python name', 'val'))
for attr in getattr(self.current, 'visa_attributes_classes', ()):
@@ -290,8 +322,28 @@ class VisaShell(Cmd):
attr_name, attr_state = args[0], args[1]
if attr_name.startswith('VI_'):
try:
- self.current.set_visa_attribute(getattr(constants, attr_name), attr_state)
- print('Done')
+ attributeId = getattr(constants, attr_name)
+ attr = attributes.AttributesByID[attributeId]
+ datatype = attr.visa_type
+ retcode = None
+ if datatype == 'ViBoolean':
+ if attr_state == 'True':
+ attr_state = True
+ elif attr_state == 'False':
+ attr_state = False
+ else:
+ retcode = constants.StatusCode.error_nonsupported_attribute_state
+ elif datatype in ['ViUInt8', 'ViUInt16', 'ViUInt32', 'ViInt8', 'ViInt16', 'ViInt32']:
+ try:
+ attr_state = int(attr_state)
+ except ValueError:
+ retcode = constants.StatusCode.error_nonsupported_attribute_state
+ if not retcode:
+ retcode = self.current.set_visa_attribute(attributeId, attr_state)
+ if retcode:
+ print('Error {}'.format(str(retcode)))
+ else:
+ print('Done')
except Exception as e:
print(e)
else:
@@ -307,6 +359,55 @@ class VisaShell(Cmd):
return [item for item in self.py_attr if item.startswith(text)] + \
[item for item in self.vi_attr if item.startswith(text)]
+ def do_termchar(self, args):
+ """Get or set termination character for resource in use.
+ <termchar> can be one of: CR, LF, CRLF, NUL or None.
+ None is used to disable termination character
+
+ Get termination character:
+
+ termchar
+
+ Set termination character read or read+write:
+
+ termchar <termchar> [<termchar>]
+
+ """
+
+ if not self.current:
+ print('There are no resources in use. Use the command "open".')
+ return
+
+ args = args.strip()
+
+ if not args:
+ try:
+ charmap = { u'\r': 'CR', u'\n': 'LF', u'\r\n': 'CRLF', u'\0': 'NUL' }
+ chr = self.current.read_termination
+ if chr in charmap:
+ chr = charmap[chr]
+ chw = self.current.write_termination
+ if chw in charmap:
+ chw = charmap[chw]
+ print('Termchar read: {} write: {}'.format(chr, chw))
+ except Exception as e:
+ print(e)
+ else:
+ args = args.split(' ')
+ charmap = { 'CR': u'\r', 'LF': u'\n', 'CRLF': u'\r\n', 'NUL': u'\0', 'None': None }
+ chr = args[0]
+ chw = args[0 if len(args) == 1 else 1]
+ if chr in charmap and chw in charmap:
+ try:
+ self.current.read_termination = charmap[chr]
+ self.current.write_termination = charmap[chw]
+ print('Done')
+ except Exception as e:
+ print(e)
+ else:
+ print('use CR, LF, CRLF, NUL or None to set termchar')
+ return
+
def do_exit(self, arg):
"""Exit the shell session."""
diff --git a/pyvisa/testsuite/__init__.py b/pyvisa/testsuite/__init__.py
index e46a262..acd8991 100644
--- a/pyvisa/testsuite/__init__.py
+++ b/pyvisa/testsuite/__init__.py
@@ -1,15 +1,18 @@
# -*- coding: utf-8 -*-
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import os
import logging
+import warnings
+import unittest
from contextlib import contextmanager
from logging.handlers import BufferingHandler
from pyvisa import logger
-from pyvisa.compat import unittest
+from pyvisa.compat import PYTHON3
class TestHandler(BufferingHandler):
@@ -62,6 +65,21 @@ class BaseTestCase(unittest.TestCase):
msg = '\n'.join(record.get('msg', str(record)) for record in buf)
self.assertEqual(l, 0, msg='%d warnings raised.\n%s' % (l, msg))
+ if not PYTHON3:
+ @contextmanager
+ def assertWarns(self, category):
+ """Backport for Python 2
+
+ """
+ with warnings.catch_warnings(record=True) as w:
+ # Cause all warnings to always be triggered.
+ warnings.simplefilter("always")
+ # Trigger a warning.
+ yield
+ # Verify some things
+ assert len(w) == 1, 'No warning raised'
+ assert issubclass(w[-1].category, category)
+
def testsuite():
"""A testsuite that has all the pyvisa tests.
@@ -85,4 +103,3 @@ def run():
"""
test_runner = unittest.TextTestRunner()
return test_runner.run(testsuite())
-
diff --git a/pyvisa/testsuite/test_errors.py b/pyvisa/testsuite/test_errors.py
new file mode 100644
index 0000000..bcecdec
--- /dev/null
+++ b/pyvisa/testsuite/test_errors.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import division, unicode_literals, print_function, absolute_import
+
+import pickle
+
+from pyvisa.testsuite import BaseTestCase
+
+from pyvisa import errors
+
+
+class TestPicleUnpickle(BaseTestCase):
+ def _test_pickle_unpickle(self, instance):
+ pickled = pickle.dumps(instance)
+ unpickled = pickle.loads(pickled)
+ self.assertIsInstance(unpickled, type(instance))
+ self.assertEqual(instance, unpickled)
+
+ def test_VisaIOError(self):
+ self._test_pickle_unpickle(errors.VisaIOError(0))
+
+ def test_VisaIOWarning(self):
+ self._test_pickle_unpickle(errors.VisaIOWarning(0))
+
+ def test_UnknownHandler(self):
+ self._test_pickle_unpickle(errors.UnknownHandler(0,0,0))
+
+ def test_OSNotSupported(self):
+ self._test_pickle_unpickle(errors.OSNotSupported(""))
+
+ def test_InvalidBinaryFormat(self):
+ self._test_pickle_unpickle(errors.InvalidBinaryFormat())
+
+ def InvalidSession(self):
+ self._test_pickle_unpickle(errors.InvalidSession())
diff --git a/pyvisa/testsuite/test_rname.py b/pyvisa/testsuite/test_rname.py
index f6c694b..55193ba 100644
--- a/pyvisa/testsuite/test_rname.py
+++ b/pyvisa/testsuite/test_rname.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
-from pyvisa.compat import unittest
+import unittest
from pyvisa.testsuite import BaseTestCase
from pyvisa import rname
diff --git a/pyvisa/testsuite/test_util.py b/pyvisa/testsuite/test_util.py
index 85ff8c7..00501eb 100644
--- a/pyvisa/testsuite/test_util.py
+++ b/pyvisa/testsuite/test_util.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
from pyvisa.testsuite import BaseTestCase
@@ -12,49 +13,102 @@ try:
except ImportError:
np = None
+
class TestParser(BaseTestCase):
def test_parse_binary(self):
- s = b'#A@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xde\x8b<@\xde\x8b<@\xde\x8b<' \
- b'@\xde\x8b<@\xe0\x8b<@\xe0\x8b<@\xdc\x8b<@\xde\x8b<@\xe2\x8b<@\xe0\x8b<'
+ s = (b'#A@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xde\x8b<@\xde\x8b<@'
+ b'\xde\x8b<@\xde\x8b<@\xe0\x8b<@\xe0\x8b<@\xdc\x8b<@\xde\x8b<@'
+ b'\xe2\x8b<@\xe0\x8b<')
e = [0.01707566, 0.01707566, 0.01707566, 0.01707566, 0.01707375,
0.01707375, 0.01707375, 0.01707375, 0.01707470, 0.01707470,
0.01707280, 0.01707375, 0.01707566, 0.01707470]
- p = util.parse_binary(s, is_big_endian=False, is_single=True)
+ with self.assertWarns(FutureWarning):
+ p = util.parse_binary(s, is_big_endian=False, is_single=True)
for a, b in zip(p, e):
self.assertAlmostEqual(a, b)
+
+ # Test handling indefinite length block
p = util.from_ieee_block(s, datatype='f', is_big_endian=False)
for a, b in zip(p, e):
self.assertAlmostEqual(a, b)
- def test_ieee_integer(self):
+ # Test handling definite length block
+ p = util.from_ieee_block(b'#214' + s[2:], datatype='f',
+ is_big_endian=False)
+ for a, b in zip(p, e):
+ self.assertAlmostEqual(a, b)
+
+ p = util.from_hp_block(b'#A\x0e\x00' + s[2:], datatype='f',
+ is_big_endian=False)
+ for a, b in zip(p, e):
+ self.assertAlmostEqual(a, b)
+
+ def test_integer_ascii_block(self):
values = list(range(99))
- containers = (list, tuple) #+ ((np.asarray,) if np else ())
- for fmt in 'bBhHiIfd':
- for endi in (True, False):
- for cont in containers:
- conv = cont(values)
- msg = 'fmt=%s, endianness=%s, container=%s' % (fmt, endi, cont.__name__)
- try:
- block = util.to_ieee_block(conv, fmt, endi)
- parsed = util.from_ieee_block(block, fmt, endi, cont)
- except Exception as e:
- raise Exception(msg + '\n' + repr(e))
-
- self.assertEqual(conv, parsed, msg)
-
- def test_ieee_noninteger(self):
+ for fmt in 'd':
+ msg = 'block=%s, fmt=%s'
+ msg = msg % ('ascii', fmt)
+ tb = lambda values: util.to_ascii_block(values, fmt, ',')
+ fb = lambda block, cont: util.from_ascii_block(block, fmt, ',',
+ cont)
+ self.round_trip_block_converstion(values, tb, fb, msg)
+
+ def test_non_integer_ascii_block(self):
values = [val + 0.5 for val in range(99)]
- containers = (list, tuple) #+ ((np.asarray,) if np else ())
- for fmt in 'fd':
- for endi in (True, False):
- for cont in containers:
- conv = cont(values)
- msg = 'fmt=%s, endianness=%s, container=%s' % (fmt, endi, cont.__name__)
- try:
- block = util.to_ieee_block(conv, fmt, endi)
- parsed = util.from_ieee_block(block, fmt, endi, cont)
- except Exception as e:
- raise Exception(msg + '\n' + repr(e))
-
- self.assertEqual(conv, parsed, msg)
+ values = list(range(99))
+ for fmt in 'fFeEgG':
+ msg = 'block=%s, fmt=%s'
+ msg = msg % ('ascii', fmt)
+ tb = lambda values: util.to_ascii_block(values, fmt, ',')
+ fb = lambda block, cont: util.from_ascii_block(block, fmt, ',',
+ cont)
+ self.round_trip_block_converstion(values, tb, fb, msg)
+
+ def test_integer_binary_block(self):
+ values = list(range(99))
+ for block, tb, fb in zip(('ieee', 'hp'),
+ (util.to_ieee_block, util.to_hp_block),
+ (util.from_ieee_block, util.from_hp_block)):
+ for fmt in 'bBhHiIfd':
+ for endi in (True, False):
+ msg = 'block=%s, fmt=%s, endianness=%s'
+ msg = msg % (block, fmt, endi)
+ tblock = lambda values: tb(values, fmt, endi)
+ fblock = lambda block, cont: fb(block, fmt, endi, cont)
+ self.round_trip_block_converstion(values, tblock, fblock,
+ msg)
+
+ def test_noninteger_binary_block(self):
+ values = [val + 0.5 for val in range(99)]
+ for block, tb, fb in zip(('ieee', 'hp'),
+ (util.to_ieee_block, util.to_hp_block),
+ (util.from_ieee_block, util.from_hp_block)):
+ for fmt in 'fd':
+ for endi in (True, False):
+ msg = 'block=%s, fmt=%s, endianness=%s'
+ msg = msg % (block, fmt, endi)
+ tblock = lambda values: bytearray(tb(values, fmt, endi))
+ fblock = lambda block, cont: fb(block, fmt, endi, cont)
+ self.round_trip_block_converstion(values, tblock, fblock,
+ msg)
+
+ def round_trip_block_converstion(self, values, to_block, from_block, msg):
+ """Test that block conversion round trip as expected.
+
+ """
+ containers = (list, tuple) + ((np.array,) if np else ())
+ for cont in containers:
+ conv = cont(values)
+ msg += ', container=%s'
+ msg = msg % cont.__name__
+ try:
+ block = to_block(conv)
+ parsed = from_block(block, cont)
+ except Exception as e:
+ raise Exception(msg + '\n' + repr(e))
+
+ if np and cont in (np.array,):
+ np.testing.assert_array_equal(conv, parsed, msg)
+ else:
+ self.assertEqual(conv, parsed, msg)
diff --git a/pyvisa/util.py b/pyvisa/util.py
index 8fcbf1f..7b01435 100644
--- a/pyvisa/util.py
+++ b/pyvisa/util.py
@@ -11,7 +11,8 @@
:license: MIT, see LICENSE for more details.
"""
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+ absolute_import)
import functools
import io
@@ -20,11 +21,34 @@ import platform
import sys
import subprocess
import warnings
+import inspect
+from subprocess import check_output
-from .compat import check_output, string_types, OrderedDict, struct
+from .compat import (string_types, OrderedDict, struct,
+ int_to_bytes, int_from_bytes, PYTHON3)
from . import __version__, logger
+try:
+ import numpy as np
+except ImportError:
+ np = None
+
+
+def _use_numpy_routines(container):
+ """Should optimized numpy routines be used to extract the data.
+
+ """
+ if np is None or container in (tuple, list):
+ return False
+
+ if (container is np.array or (inspect.isclass(container) and
+ issubclass(container, np.ndarray))):
+ return True
+
+ return False
+
+
def read_user_library_path():
"""Return the library path stored in one of the following configuration files:
@@ -43,13 +67,16 @@ def read_user_library_path():
"""
try:
- from ConfigParser import SafeConfigParser as ConfigParser, NoSectionError
+ from ConfigParser import (SafeConfigParser as ConfigParser,
+ NoSectionError)
except ImportError:
from configparser import ConfigParser, NoSectionError
config_parser = ConfigParser()
- files = config_parser.read([os.path.join(sys.prefix, "share", "pyvisa", ".pyvisarc"),
- os.path.join(os.path.expanduser("~"), ".pyvisarc")])
+ files = config_parser.read([os.path.join(sys.prefix, "share", "pyvisa",
+ ".pyvisarc"),
+ os.path.join(os.path.expanduser("~"),
+ ".pyvisarc")])
if not files:
logger.debug('No user defined library files')
@@ -151,6 +178,16 @@ _converters = {
'G': float,
}
+_np_converters = {
+ 'd': 'i',
+ 'e': 'f',
+ 'E': 'f',
+ 'f': 'f',
+ 'F': 'f',
+ 'g': 'f',
+ 'G': 'f',
+}
+
def from_ascii_block(ascii_data, converter='f', separator=',', container=list):
"""Parse ascii data and return an iterable of numbers.
@@ -165,12 +202,19 @@ def from_ascii_block(ascii_data, converter='f', separator=',', container=list):
:type: separator: (str) -> collections.Iterable[T] | str
:param container: container type to use for the output data.
"""
+ if (_use_numpy_routines(container) and
+ isinstance(converter, string_types) and
+ isinstance(separator, string_types) and
+ converter in _np_converters):
+ return np.fromstring(ascii_data, _np_converters[converter],
+ sep=separator)
if isinstance(converter, string_types):
try:
converter = _converters[converter]
except KeyError:
- raise ValueError('Invalid code for converter: %s not in %s' % (converter, str(tuple(_converters.keys()))))
+ raise ValueError('Invalid code for converter: %s not in %s' %
+ (converter, str(tuple(_converters.keys()))))
if isinstance(separator, string_types):
data = ascii_data.split(separator)
@@ -181,7 +225,7 @@ def from_ascii_block(ascii_data, converter='f', separator=',', container=list):
def to_ascii_block(iterable, converter='f', separator=','):
- """Parse ascii data and return an iterable of numbers.
+ """Turn an iterable of numbers in an ascii block of data.
:param iterable: data to be parsed.
:type iterable: collections.Iterable[T]
@@ -192,6 +236,8 @@ def to_ascii_block(iterable, converter='f', separator=','):
:param separator: a callable that split the str into individual elements.
If a str is given, data.split(separator) is used.
:type: separator: (collections.Iterable[T]) -> str | str
+
+ :rtype: str
"""
if isinstance(separator, string_types):
@@ -199,9 +245,10 @@ def to_ascii_block(iterable, converter='f', separator=','):
if isinstance(converter, string_types):
converter = '%' + converter
- return separator(converter % val for val in iterable)
+ block = separator(converter % val for val in iterable)
else:
- return separator(converter(val) for val in iterable)
+ block = separator(converter(val) for val in iterable)
+ return block
def parse_binary(bytes_data, is_big_endian=False, is_single=False):
@@ -214,6 +261,9 @@ def parse_binary(bytes_data, is_big_endian=False, is_single=False):
:param is_single: boolean indicating the type (if not is double)
:return:
"""
+ warnings.warn('parse_binary is deprecated and will be removed in '
+ '1.10, use read_ascii_values or read_binary_values '
+ 'instead.', FutureWarning)
data = bytes_data
hash_sign_position = bytes_data.find(b"#")
@@ -269,20 +319,20 @@ def parse_ieee_block_header(block):
#0<data>
:param block: IEEE block.
- :type block: bytes
+ :type block: bytes | bytearray
:return: (offset, data_length)
:rtype: (int, int)
"""
begin = block.find(b'#')
if begin < 0:
- raise ValueError("Could not find hash sign (#) indicating the start of the block.")
+ raise ValueError("Could not find hash sign (#) indicating the start of"
+ " the block.")
try:
# int(block[begin+1]) != int(block[begin+1:begin+2]) in Python 3
header_length = int(block[begin+1:begin+2])
except ValueError:
header_length = 0
-
offset = begin + 2 + header_length
if header_length > 0:
@@ -297,6 +347,35 @@ def parse_ieee_block_header(block):
return offset, data_length
+def parse_hp_block_header(block, is_big_endian):
+ """Parse the header of a HP block.
+
+ Definite Length Arbitrary Block:
+ #A<data_length><data>
+
+ The header ia always 4 bytes long.
+ The data_length field specifies the size of the data.
+
+ :param block: HP block.
+ :type block: bytes | bytearray
+ :param is_big_endian: boolean indicating endianess.
+ :return: (offset, data_length)
+ :rtype: (int, int)
+
+ """
+ begin = block.find(b'#A')
+ if begin < 0:
+ raise ValueError("Could not find the standard block header (#A) "
+ "indicating the start of the block.")
+ offset = begin + 4
+
+ data_length = int_from_bytes(block[begin+2:offset],
+ byteorder='big' if is_big_endian else 'little'
+ )
+
+ return offset, data_length
+
+
def from_ieee_block(block, datatype='f', is_big_endian=False, container=list):
"""Convert a block in the IEEE format into an iterable of numbers.
@@ -309,38 +388,68 @@ def from_ieee_block(block, datatype='f', is_big_endian=False, container=list):
Indefinite Length Arbitrary Block:
#0<data>
- :param block: IEEE block.
- :type block: bytes
+ :param block: HP block.
+ :type block: bytes | bytearray
:param datatype: the format string for a single element. See struct module.
:param is_big_endian: boolean indicating endianess.
:param container: container type to use for the output data.
:return: items
:rtype: type(container)
"""
-
offset, data_length = parse_ieee_block_header(block)
if len(block) < offset + data_length:
- raise ValueError("Binary data is incomplete. The header states %d data bytes, "
- "but %d where received." % (data_length, len(block) - offset))
+ raise ValueError("Binary data is incomplete. The header states %d data"
+ " bytes, but %d where received." %
+ (data_length, len(block) - offset))
+
+ return from_binary_block(block, offset, data_length, datatype,
+ is_big_endian, container)
- return from_binary_block(block, offset, data_length, datatype, is_big_endian, container)
+def from_hp_block(block, datatype='f', is_big_endian=False, container=list):
+ """Convert a block in the HP format into an iterable of numbers.
-def from_binary_block(block, offset=0, data_length=None, datatype='f', is_big_endian=False, container=list):
+ Definite Length Arbitrary Block:
+ #A<data_length><data>
+
+ The header ia always 4 bytes long.
+ The data_length field specifies the size of the data.
+
+ :param block: IEEE block.
+ :type block: bytes | bytearray
+ :param datatype: the format string for a single element. See struct module.
+ :param is_big_endian: boolean indicating endianess.
+ :param container: container type to use for the output data.
+ :return: items
+ :rtype: type(container)
+ """
+ offset, data_length = parse_hp_block_header(block, is_big_endian)
+
+ if len(block) < offset + data_length:
+ raise ValueError("Binary data is incomplete. The header states %d data"
+ " bytes, but %d where received." %
+ (data_length, len(block) - offset))
+
+ return from_binary_block(block, offset, data_length, datatype,
+ is_big_endian, container)
+
+
+def from_binary_block(block, offset=0, data_length=None, datatype='f',
+ is_big_endian=False, container=list):
"""Convert a binary block into an iterable of numbers.
:param block: binary block.
- :type block: bytes
+ :type block: bytes | bytearray
:param offset: offset at which the data block starts (default=0)
- :param data_length: size in bytes of the data block (default=len(block) - offset)
+ :param data_length: size in bytes of the data block
+ (default=len(block) - offset)
:param datatype: the format string for a single element. See struct module.
:param is_big_endian: boolean indicating endianess.
:param container: container type to use for the output data.
:return: items
:rtype: type(container)
"""
-
if data_length is None:
data_length = len(block) - offset
@@ -349,6 +458,9 @@ def from_binary_block(block, offset=0, data_length=None, datatype='f', is_big_en
endianess = '>' if is_big_endian else '<'
+ if _use_numpy_routines(container):
+ return np.frombuffer(block, endianess+datatype, array_length, offset)
+
fullfmt = '%s%d%s' % (endianess, array_length, datatype)
try:
@@ -357,6 +469,27 @@ def from_binary_block(block, offset=0, data_length=None, datatype='f', is_big_en
raise ValueError("Binary data was malformed")
+def to_binary_block(iterable, header, datatype, is_big_endian):
+ """Convert an iterable of numbers into a block of data with a given header.
+
+ :param iterable: an iterable of numbers.
+ :param header: the header which should prefix the binary block
+ :param datatype: the format string for a single element. See struct module.
+ :param is_big_endian: boolean indicating endianess.
+ :return: IEEE block.
+ :rtype: bytes
+ """
+ array_length = len(iterable)
+
+ endianess = '>' if is_big_endian else '<'
+ fullfmt = '%s%d%s' % (endianess, array_length, datatype)
+
+ if isinstance(header, string_types):
+ header = bytes(header, 'ascii') if PYTHON3 else str(header)
+
+ return header + struct.pack(fullfmt, *iterable)
+
+
def to_ieee_block(iterable, datatype='f', is_big_endian=False):
"""Convert an iterable of numbers into a block of data in the IEEE format.
@@ -366,21 +499,34 @@ def to_ieee_block(iterable, datatype='f', is_big_endian=False):
:return: IEEE block.
:rtype: bytes
"""
-
array_length = len(iterable)
element_length = struct.calcsize(datatype)
data_length = array_length * element_length
header = '%d' % data_length
- header = '#%d%s'%(len(header),header)
+ header = '#%d%s' % (len(header), header)
- endianess = '>' if is_big_endian else '<'
- fullfmt = '%s%d%s' % (endianess, array_length, datatype)
+ return to_binary_block(iterable, header, datatype, is_big_endian)
- if sys.version >= '3':
- return bytes(header, 'ascii') + struct.pack(fullfmt, *iterable)
- else:
- return str(header) + struct.pack(fullfmt, *iterable)
+
+def to_hp_block(iterable, datatype='f', is_big_endian=False):
+ """Convert an iterable of numbers into a block of data in the HP format.
+
+ :param iterable: an iterable of numbers.
+ :param datatype: the format string for a single element. See struct module.
+ :param is_big_endian: boolean indicating endianess.
+ :return: IEEE block.
+ :rtype: bytes
+ """
+
+ array_length = len(iterable)
+ element_length = struct.calcsize(datatype)
+ data_length = array_length * element_length
+
+ header = b'#A' + (int_to_bytes(data_length, 2,
+ 'big' if is_big_endian else 'little'))
+
+ return to_binary_block(iterable, header, datatype, is_big_endian)
def get_system_details(backends=True):
@@ -530,7 +676,7 @@ machine_types = {
0x0200: 'IA64',
0x0266: 'MIPS16',
0x0284: 'ALPHA64',
- #0x0284: 'AXP64', # same
+ # 0x0284: 'AXP64', # same
0x0366: 'MIPSFPU',
0x0466: 'MIPSFPU16',
0x0520: 'TRICORE',
@@ -573,7 +719,7 @@ def get_arch(filename):
return 64,
else:
return ()
- elif not this_platform in ('linux2', 'linux3', 'linux', 'darwin'):
+ elif this_platform not in ('linux2', 'linux3', 'linux', 'darwin'):
raise OSError('')
out = check_output(["file", filename], stderr=subprocess.STDOUT)
diff --git a/setup.py b/setup.py
index 4df01b9..43c0732 100644
--- a/setup.py
+++ b/setup.py
@@ -15,16 +15,15 @@ except ImportError:
sys.exit(1)
-import codecs
-
-
def read(filename):
- return codecs.open(filename, encoding='utf-8').read()
+ with open(filename, 'rb') as f:
+ return f.read()
+a2 = read('AUTHORS').decode('utf-8')
+a3 = read('CHANGES').decode('utf-8')
+a1 = read('README').decode('utf-8')
-long_description = '\n\n'.join([read('README'),
- read('AUTHORS'),
- read('CHANGES')])
+long_description = '\n\n'.join([a1,a2,a3])
__doc__ = long_description
@@ -32,21 +31,19 @@ requirements = []
if sys.version_info < (3, 4):
requirements.append('enum34')
-if sys.version_info < (2, 7):
- requirements.append('unittest2')
-
setup(name='PyVISA',
- description='Python VISA bindings for GPIB, RS232, and USB instruments',
- version='1.8',
+ description='Python VISA bindings for GPIB, RS232, TCPIP and USB instruments',
+ version='1.9.0',
long_description=long_description,
author='Torsten Bronger, Gregor Thalhammer',
author_email='bronger@physik.rwth-aachen.de',
maintainer='Hernan E. Grecco',
maintainer_email='hernan.grecco@gmail.com',
- url='https://github.com/hgrecco/pyvisa',
+ url='https://github.com/pyvisa/pyvisa',
test_suite='pyvisa.testsuite.testsuite',
keywords='VISA GPIB USB serial RS232 measurement acquisition',
license='MIT License',
+ python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
install_requires=requirements,
classifiers=[
'Development Status :: 5 - Production/Stable',
@@ -59,11 +56,12 @@ setup(name='PyVISA',
'Programming Language :: Python',
'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator',
'Topic :: Software Development :: Libraries :: Python Modules',
- 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3.2',
- 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
],
packages=['pyvisa', 'pyvisa.compat',
'pyvisa.ctwrapper',
@@ -71,6 +69,9 @@ setup(name='PyVISA',
'pyvisa.thirdparty',
'pyvisa.testsuite'],
platforms="Linux, Windows,Mac",
+ entry_points={'console_scripts':
+ ['pyvisa-shell=visa:visa_shell',
+ 'pyvisa-info=visa:visa_info']},
py_modules=['visa'],
use_2to3=False,
zip_safe=False)
diff --git a/tox.ini b/tox.ini
index 6478ef5..c89b91e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,9 @@
[tox]
-envlist = py26,py27,py32,py33
+envlist = py27,py34,py35,py36
[testenv]
deps=
- pytest
+ pytest
sphinx
mock
diff --git a/visa.py b/visa.py
index 120c433..6e1a164 100644
--- a/visa.py
+++ b/visa.py
@@ -21,19 +21,35 @@ from pyvisa.errors import (Error, VisaIOError, VisaIOWarning, VisaTypeError,
# This is needed to registry all resources.
from pyvisa.resources import Resource
-if __name__ == '__main__':
+def visa_main(command=None):
import argparse
parser = argparse.ArgumentParser(description='PyVISA command-line utilities')
- subparsers = parser.add_subparsers(title='command', dest='command')
- info_parser = subparsers.add_parser('info', help='print information to diagnose PyVISA')
+ parser.add_argument('--backend', '-b', dest='backend', action='store', default=None,
+ help='backend to be used (default: ni)')
+
+ if not command:
+ subparsers = parser.add_subparsers(title='command', dest='command')
- console_parser = subparsers.add_parser('shell', help='start the PyVISA console')
+ info_parser = subparsers.add_parser('info', help='print information to diagnose PyVISA')
+
+ console_parser = subparsers.add_parser('shell', help='start the PyVISA console')
args = parser.parse_args()
+ if command:
+ args.command = command
if args.command == 'info':
from pyvisa import util
util.get_debug_info()
elif args.command == 'shell':
from pyvisa import shell
- shell.main()
+ shell.main('@' + args.backend if args.backend else '')
+
+def visa_shell():
+ visa_main('shell')
+
+def visa_info():
+ visa_main('info')
+
+if __name__ == '__main__':
+ visa_main()