New Upstream Release - pyvisa
Ready changes
Summary
Merged new upstream version: 1.13.0 (was: 1.11.3).
Diff
diff --git a/.coveragerc b/.coveragerc
index 7639f91..871b78e 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -13,5 +13,5 @@ exclude_lines =
pragma: no cover
# Don't complain if tests don't hit defensive assertion code:
- raise NotImplementedError
+ raise NotImplementedError()
pass
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..4a98a43
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,18 @@
+[flake8]
+exclude =
+ .git,
+ __pycache__,
+ docs/source/conf.py,
+ old,
+ build,
+ dist,
+ pyvisa/thirdparty/*,
+ignore = E203, E266, E501, W503, E731
+# line length is intentionally set to 80 here because pyvisa uses Bugbear
+# See https://github.com/psf/black/blob/master/README.md#line-length for more details
+max-line-length = 80
+max-complexity = 18
+select = B,C,E,F,W,T4,B9
+per-file-ignores =
+ pyvisa/__init__.py:E402
+ pyvisa/constants.py:E221
\ No newline at end of file
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..db00e4e
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: [MatthieuDartiailh]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..3bad94f
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,7 @@
+version: 2
+updates:
+ # Maintain dependencies for GitHub Actions
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 86c5855..0fca6cd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,20 +1,19 @@
name: Continuous Integration
on:
schedule:
- - cron: '0 0 * * 2'
+ - cron: "0 0 * * 2"
push:
branches:
- - master
+ - main
- staging
- trying
pull_request:
branches:
- - master
+ - main
paths:
- .github/workflows/ci.yml
- - pyvisa/*
+ - "pyvisa/**"
- pyproject.toml
- - setup.cfg
- setup.py
jobs:
@@ -22,52 +21,59 @@ jobs:
name: Check code formatting
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up Python
- uses: actions/setup-python@v1
+ uses: actions/setup-python@v4
with:
- python-version: 3.8
+ python-version: "3.10"
- name: Install tools
run: |
python -m pip install --upgrade pip
- pip install flake8 black isort mypy pytest
+ pip install flake8 black isort mypy pytest numpy
- name: Isort
run: |
isort pyvisa -c;
- name: Black
+ if: always()
run: |
black pyvisa --check;
- name: Flake8
+ if: always()
run: |
flake8 pyvisa;
- name: Mypy
+ if: always()
run: |
mypy pyvisa;
tests:
name: Unit tests
runs-on: ${{ matrix.os }}
+ needs:
+ - formatting
+ if: needs.formatting.result == 'success'
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
- python-version: [3.6, 3.7, 3.8, 3.9]
+ python-version: ["3.8", "3.9", "3.10", "3.11"]
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v1
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
+ pip install numpy
- name: Install project
run: |
pip install -e .
- name: Test with pytest
run: |
pip install pytest-cov
- pytest --pyargs pyvisa --cov pyvisa --cov-report xml -v
+ python -X dev -m pytest --pyargs pyvisa --cov --cov-report xml -v
- name: Upload coverage to Codecov
- uses: codecov/codecov-action@v1
+ uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests
@@ -88,4 +94,4 @@ jobs:
run: exit 0
- name: Mark the job as a failure
if: needs.tests.result != 'success'
- run: exit 1
\ No newline at end of file
+ run: exit 1
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 8a69f55..6e3f3a8 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -1,19 +1,19 @@
name: Documentation building
on:
schedule:
- - cron: '0 0 * * 2'
+ - cron: "0 0 * * 2"
push:
branches:
- - master
+ - main
- staging
- trying
pull_request:
branches:
- - master
+ - main
paths:
- .github/workflows/docs.yml
- - pyvisa/*
- - docs/*
+ - "pyvisa/**"
+ - "docs/**"
- setup.py
jobs:
@@ -21,20 +21,18 @@ jobs:
name: Docs building
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up Python
- uses: actions/setup-python@v1
+ uses: actions/setup-python@v4
- name: Install dependencies
run: |
python -m pip install --upgrade pip
+ pip install -r docs/requirements.txt
- name: Install project
run: |
- python setup.py develop
+ pip install .
- name: Install graphviz
- uses: kamiazya/setup-graphviz@v1
- - name: Install doc building tools
- run: |
- pip install sphinx sphinx_rtd_theme
+ uses: ts-graphviz/setup-graphviz@v1
- name: Build documentation
run: |
mkdir docs_output;
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..8e3e137
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,111 @@
+name: Build and upload wheels
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '0 0 * * 3'
+ push:
+ tags:
+ - '*'
+
+jobs:
+ build_sdist:
+ name: Build sdist
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Get history and tags for SCM versioning to work
+ run: |
+ git fetch --prune --unshallow
+ git fetch --depth=1 origin +refs/tags/*:refs/tags/*
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ - name: Build sdist
+ run: |
+ pip install --upgrade pip
+ pip install wheel build
+ python -m build . -s
+ - name: Test sdist
+ run: |
+ pip install pytest
+ pip install dist/*.tar.gz
+ python -X dev -m pytest --pyargs pyvisa
+ - name: Store artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: artifact
+ path: dist/*
+
+ build_wheel:
+ name: Build wheel
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Get history and tags for SCM versioning to work
+ run: |
+ git fetch --prune --unshallow
+ git fetch --depth=1 origin +refs/tags/*:refs/tags/*
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ - name: Build wheels
+ run: |
+ pip install --upgrade pip
+ pip install wheel build
+ python -m build . -w
+ - name: Test wheel
+ run: |
+ pip install pytest
+ pip install dist/*.whl
+ python -X dev -m pytest --pyargs pyvisa
+ - name: Store artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: artifact
+ path: dist/*.whl
+
+ release_upload:
+ name: Create Release and Upload Release Asset
+ runs-on: ubuntu-latest
+ if: github.event_name == 'push'
+ needs: [build_wheel, build_sdist]
+ steps:
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Release ${{ github.ref }}
+ draft: false
+ prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'a') || contains(github.ref, 'b')}}
+ - uses: actions/download-artifact@v3
+ with:
+ name: artifact
+ path: dist
+ - name: Upload Release Asset
+ id: upload-release-asset
+ uses: shogo82148/actions-upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: dist/*
+
+ upload_pypi:
+ if: github.event_name == 'push'
+ needs: [build_wheel, build_sdist]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/download-artifact@v3
+ with:
+ name: artifact
+ path: dist
+
+ - uses: pypa/gh-action-pypi-publish@master
+ with:
+ user: __token__
+ password: ${{ secrets.pypi_password }}
+ # To test:
+ # repository_url: https://test.pypi.org/legacy/
diff --git a/.gitignore b/.gitignore
index 42e53bb..fbeea44 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,7 @@ _test/
htmlcov/
.mypy_cache/
pip-wheel-metadata/
+.pytest_cache
+.venv/
+venv/
+.dmypy.json
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0b11493..45f6be3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,19 +1,18 @@
repos:
-- repo: https://github.com/pre-commit/mirrors-isort
- rev: v5.5.3
- hooks:
- - id: isort
-- repo: https://github.com/psf/black
- rev: 20.8b1
- hooks:
- - id: black
- language_version: python3.7
-- repo: https://gitlab.com/pycqa/flake8
- rev: 3.8.3
- hooks:
- - id: flake8
-- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.782 # Use the sha / tag you want to point at
- hooks:
- - id: mypy
- additional_dependencies: [numpy, typing_extensions]
\ No newline at end of file
+ - repo: https://github.com/pre-commit/mirrors-isort
+ rev: v5.10.1
+ hooks:
+ - id: isort
+ - repo: https://github.com/psf/black
+ rev: 22.10.0
+ hooks:
+ - id: black
+ - repo: https://github.com/pycqa/flake8
+ rev: 6.0.0
+ hooks:
+ - id: flake8
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v0.991 # Use the sha / tag you want to point at
+ hooks:
+ - id: mypy
+ additional_dependencies: [numpy, typing_extensions]
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..ea5c064
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,27 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the version of Python and other tools you might need
+build:
+ os: ubuntu-20.04
+ tools:
+ python: "3.10"
+
+# Build documentation in the docs/source directory with Sphinx
+sphinx:
+ configuration: docs/source/conf.py
+
+# Enable epub output
+formats:
+ - epub
+
+# Optionally declare the Python requirements required to build your docs
+python:
+ install:
+ - requirements: docs/requirements.txt
+ - method: pip
+ path: .
diff --git a/AUTHORS b/AUTHORS
index cb050ed..c36abdc 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,7 +2,8 @@ PyVISA was originally programmed by Torsten Bronger and Gregor Thalhammer, Innsb
It is based on earlier experiences by Thalhammer.
It was maintained from March 2012 to August 2013 by Florian Bauer.
-It is currently maintained by Hernan E. Grecco <hernan.grecco@gmail.com>.
+It was maintained till 2018 by Hernan E. Grecco
+It is currently maintained by Matthieu Dartiailh <m.dartiailh@gmail.com>.
Other contributors, listed alphabetically, are:
diff --git a/CHANGES b/CHANGES
index 814aba0..e56f65f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,50 @@
PyVISA Changelog
================
+1.13.0 (22-12-2022)
+-------------------
+
+- add official support for Python 3.11 and drop support for Python 3.7
+- add support for VICP resource names PR #699
+ NOTE: the interface type enum value for VICP is unknown. No documentation was
+ found and tests using the VICP passport were not conclusive. If somebody figures
+ out the right value it would be great
+- numerous bug fixes related to VISA attributes PR #697
+ This included fixing several typos, uncommenting the
+ AttrVI_ATTR_INTF_PARENT_NUM class, and adding in the
+ AttrVI_ATTR_RM_SESSION class,
+
+1.12.0 (11-05-2022)
+-------------------
+
+- remove deprecated feature planned for removal PR #600
+ This include the visa.py module. `
+ import visa` should be replaced by `import pyvisa as visa` and usage of
+ `python -m visa info` by `pyvisa-info` and `python -m visa shell` by `pyvisa-shell`
+- optimize write_binary_values when passing in bytes, bytearray or numpy arrays PR #668
+ We avoid going through the struct module which can cause large memory overheads.
+- fix collection of debug info on the ctypes wrapper PR #598
+- allow an IEEE binary block or an HP block to be empty PR #581
+ This is more correct and can affect real instruments. However this introduces
+ a minor backward incompatibility when parsing IEEE header. The data length for
+ #0 is now reported to be -1 instead of 0 since 0 corresponds to #10.
+ This changes should affect a minority of user and does not change the behavior for
+ instrument returning #0 (only the value of data_length as returned by
+ parse_ieee_block_header is affected and functions using it have been adapted).
+- allow trailing separators in ascii blocks PR #581
+ Numpy style extraction already handled this case correctly but it was not so
+ for the builtin parser.
+- adding open_resource attribute check for VisaLibraryBase in order to support
+ custom resource opening #660
+- changed constant ControlFlow enum type from IntEnum to IntFlag PR#652
+ This change also triggers a change in attributes.py to include a new Attribute
+ class, FlagAttribute where the enum type is declared at IntFlag.
+ Class AttrVI_ATTR_ASRL_FLOW_CNTRL now inherits from FlagAttribute.
+ Flow control attribute per ivi foundation definition is a flag and
+ multiple flow control types can be set.
+- move all setup metadata to pyproject.toml PR #600
+- remove duplicate call to the command loop after running pyvisa-shell PR #687
+
1.11.3 (07-11-2020)
-------------------
@@ -20,6 +64,7 @@ PyVISA Changelog
- allow a None value for the board value of a ResourceInfo PR #547
This allows for funky resource name such ASRL/dev/tty0::INSTR which are common
in pyvisa-py and avoid returning a completely generic resource in those cases.
+- documentation improvements for pyvisa.resources.SerialInstrument PR #558
1.11 (16-09-2020)
-----------------
diff --git a/LICENSE b/LICENSE
index 7f5275f..6907de5 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License
-Copyright (c) 2005-2019 PyVISA Authors and contributors. See AUTHORS
+Copyright (c) 2005-2022 PyVISA Authors and contributors. See AUTHORS
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
diff --git a/README.rst b/README.rst
index 692a2fe..96bfcf7 100644
--- a/README.rst
+++ b/README.rst
@@ -8,10 +8,10 @@ PyVISA
.. image:: https://github.com/pyvisa/pyvisa/workflows/Documentation%20building/badge.svg
:target: https://github.com/pyvisa/pyvisa/actions
:alt: Documentation building
-.. image:: https://dev.azure.com/pyvisa/pyvisa/_apis/build/status/pyvisa.keysight-assisted?branchName=master
+.. image:: https://dev.azure.com/pyvisa/pyvisa/_apis/build/status/pyvisa.keysight-assisted?branchName=main
:target: https://dev.azure.com/pyvisa/pyvisa/_build
:alt: Keysight assisted testing
-.. image:: https://codecov.io/gh/pyvisa/pyvisa/branch/master/graph/badge.svg
+.. image:: https://codecov.io/gh/pyvisa/pyvisa/branch/main/graph/badge.svg
:target: https://codecov.io/gh/pyvisa/pyvisa
:alt: Code Coverage
.. image:: https://readthedocs.org/projects/pyvisa/badge/?version=latest
@@ -37,7 +37,7 @@ different protocols, sent over many different interfaces and bus systems
find libraries that support both your device and its bus system.
In order to ease this unfortunate situation, the Virtual Instrument Software
-Architecture (VISA_) specification was defined in the middle of the 90'. Today
+Architecture (VISA_) specification was defined in the middle of the 90's. Today
VISA is implemented on all significant operating systems. A couple of vendors
offer VISA libraries, partly with free download. These libraries work together
with arbitrary peripheral devices, although they may be limited to certain
@@ -61,13 +61,13 @@ can also serve as a front-end for other VISA implementation such as
VISA and Python
---------------
-Python has a couple of features that make it very interesting for measurement
-controlling:
+Python has a couple of features that make it very interesting for controlling
+instruments:
- Python is an easy-to-learn scripting language with short development cycles.
- It represents a high abstraction level [2], which perfectly blends with the
abstraction level of measurement programs.
-- It has a very rich set of native libraries, including numerical and plotting
+- It has a rich set of native libraries, including numerical and plotting
modules for data analysis and visualisation.
- A large set of books (in many languages) and on-line publications is
available.
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index ff3cd23..faa3943 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -6,62 +6,60 @@
trigger:
branches:
include:
- - master
- - staging
- - trying
+ - main
+ - staging
+ - trying
pr:
-- master
+ - main
variables:
PYVISA_KEYSIGHT_VIRTUAL_INSTR: 1
-
pool:
- name: default
+ name: Keysight-based
demands: KEYSIGHT -equals TCPIP
steps:
-- script: |
- echo Activate conda
- call $(CONDA_PATH)\activate.bat
- echo Create environment
- conda create -n test_ python=3.7 numpy --yes
- displayName: 'Create environment'
+ - script: |
+ export PATH="$HOME/miniconda3/bin:$PATH"
+ echo Create environment
+ conda create -n test_ python=3.9 numpy --yes
+ displayName: "Create environment"
-- script: |
- echo Activate conda
- call $(CONDA_PATH)\activate.bat
- echo Activate environment
- call conda activate test_
- echo Install project
- pip install -e .
- displayName: 'Install dependencies'
+ - script: |
+ export PATH="$HOME/miniconda3/bin:$PATH"
+ source $HOME/miniconda3/bin/activate
+ echo Activate environment
+ conda activate test_
+ echo Install project
+ pip install -e .
+ displayName: "Install dependencies"
-- script: |
- echo Activate conda
- call $(CONDA_PATH)\activate.bat
- echo Activate environment
- call conda activate test_
- echo Install pytest and co
- pip install pytest pytest-azurepipelines pytest-cov
- echo Run pytest
- python -X dev -m pytest --pyargs pyvisa --cov pyvisa --cov-report xml -v
- displayName: 'Run tests'
+ - script: |
+ export PATH="$HOME/miniconda3/bin:$PATH"
+ source $HOME/miniconda3/bin/activate
+ echo Activate environment
+ conda activate test_
+ echo Install pytest and co
+ pip install pytest pytest-azurepipelines pytest-cov
+ echo Run pytest
+ python -X dev -m pytest --pyargs pyvisa --cov pyvisa --cov-report xml -v
+ displayName: "Run tests"
-- script: |
- echo Activate conda
- call $(CONDA_PATH)\activate.bat
- echo Activate environment
- call conda activate test_
- echo Install codecov
- pip install codecov
- echo Run codecov
- codecov --file coverage.xml --token $(CODECOV_TOKEN) --env PYVISA_KEYSIGHT_VIRTUAL_INSTR --tries 5 --required -F unittests --name codecov-umbrella
- displayName: 'Upload test coverage results'
+ - script: |
+ export PATH="$HOME/miniconda3/bin:$PATH"
+ source $HOME/miniconda3/bin/activate
+ echo Activate environment
+ conda activate test_
+ echo Install codecov
+ pip install codecov
+ echo Run codecov
+ codecov --file coverage.xml --token $(CODECOV_TOKEN) --env PYVISA_KEYSIGHT_VIRTUAL_INSTR --tries 5 --required -F unittests --name codecov-umbrella
+ displayName: "Upload test coverage results"
-- script: |
- call $(CONDA_PATH)\activate.bat
- conda remove -n test_ --all --yes
- displayName: 'Remove test environment'
- condition: always()
+ - script: |
+ export PATH="$HOME/miniconda3/bin:$PATH"
+ conda remove -n test_ --all --yes
+ displayName: "Remove test environment"
+ condition: always()
diff --git a/debian/changelog b/debian/changelog
index 45af78b..6e1e154 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pyvisa (1.13.0-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Mon, 13 Mar 2023 13:38:50 -0000
+
pyvisa (1.11.3-3) unstable; urgency=medium
[ Debian Janitor ]
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..ead9e81
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,2 @@
+sphinx>=4
+sphinx-rtd-theme>=1
diff --git a/docs/source/advanced/backends.rst b/docs/source/advanced/backends.rst
index 4d60245..cb9a930 100644
--- a/docs/source/advanced/backends.rst
+++ b/docs/source/advanced/backends.rst
@@ -91,6 +91,12 @@ As a **very minimum** set you need:
(you can get the signature below or here :ref:`api_visalibrarybase`)
+.. note::
+
+ A |ResourceManager| attribute check for
+ :class:`pyvisa.highlevel.VisaLibraryBase.open_resource` is implemented
+ in order to support custom opening handling by the new backend implementation.
+
But of course you cannot do anything interesting with just this. In general you
will also need:
diff --git a/docs/source/faq/faq.rst b/docs/source/faq/faq.rst
index 2c61bfb..f90133d 100644
--- a/docs/source/faq/faq.rst
+++ b/docs/source/faq/faq.rst
@@ -57,11 +57,12 @@ This error occurs when you have provided an invalid path for the VISA library.
Check that the path provided to the constructor or in the configuration file
-Error: Could not found VISA library
------------------------------------
+Error: Could not find VISA library
+----------------------------------
This error occurs when you have not provided a path for the VISA library and
-PyVISA is not able to find it for you. You can solve it by providing the
+PyVISA is not able to find it for you. You can solve it by creating a configuration
+file as described in :ref:`intro-configuring` (recommended) or by providing the
library path to the ``VisaLibrary`` or ``ResourceManager`` constructor::
>>> visalib = VisaLibrary('/path/to/library')
@@ -70,7 +71,32 @@ or::
>>> rm = ResourceManager('Path to library')
-or creating a configuration file as described in :ref:`intro-configuring`.
+.. note::
+
+ If you get this error while trying to create a ``ResourceManager`` in Python built
+ for Cygwin (https://www.cygwin.com/) on Windows:
+
+ 1. Check you are running the Cygwin build of Python by running ``python -VV``. If not, follow
+ the troubleshooting steps for Windows::
+
+ $ python -VV
+ Python 3.9.10 (main, Jan 20 2022, 21:37:52)
+ [GCC 11.2.0]
+
+ 2. Specify the location of the ``visa32.dll`` or ``visa64.dll`` using the ``linux`` syntax
+ and Cygwin paths by creating a `.pyvisarc` (:ref:`intro-configuring`) file::
+
+ $ cat ~/.pyvisarc
+ [Paths]
+ VISA library: /cygdrive/c/Windows/System32/visa64.dll
+
+ or::
+
+ rm = visa.ResourceManager('/cygdrive/c/Windows/System32/visa64.dll')
+
+ or::
+
+ rm = visa.ResourceManager('/cygdrive/c/Windows/System32/visa32.dll')
Error: `visa` module has no attribute `ResourceManager`
@@ -165,7 +191,7 @@ as described in :ref:`intro-configuring`.
VisaIOError: VI_ERROR_SYSTEM_ERROR: Unknown system error:
---------------------------------------------------------
-If you have an issue creating a pyvisa.ResourceManager object, first enable screen
+If you have an issue creating a pyvisa.ResourceManager object, first enable screen
logging (pyvisa.log_to_screen()) to ensure it is correctly finding the dll files.
If it is correctly finding the dlls, you may see an error similar to:
* viOpenDefaultRM('<ViObject object at 0x000002B6CA4658C8>',) -> -1073807360
@@ -205,7 +231,7 @@ Where can I get more information about VISA?
http://digital.ni.com/manuals.nsf/websearch/266526277DFF74F786256ADC0065C50C
-.. _`AUTHORS`: https://github.com/pyvisa/pyvisa/blob/master/AUTHORS
+.. _`AUTHORS`: https://github.com/pyvisa/pyvisa/blob/main/AUTHORS
.. _`Issue Tracker`: https://github.com/pyvisa/pyvisa/issues
.. _`virtual environment`: http://www.virtualenv.org/en/latest/
diff --git a/docs/source/introduction/communication.rst b/docs/source/introduction/communication.rst
index de045a6..5c6139f 100644
--- a/docs/source/introduction/communication.rst
+++ b/docs/source/introduction/communication.rst
@@ -48,6 +48,7 @@ to filter the instruments discovered by this method. The syntax is described in
details in |list_resources|. The default value is '?*::INSTR' which means that
by default only instrument whose resource name ends with '::INSTR' are listed
(in particular USB RAW resources and TCPIP SOCKET resources are not listed).
+To list all resources present, pass '?*' to ``list_resources``.
In this case, there is a GPIB instrument with instrument number 14, so you ask
the ``ResourceManager`` to open "'GPIB0::14::INSTR'" and assign the returned
@@ -116,6 +117,19 @@ start communicating as follows::
Here we use `'\n'` known as 'line feed'. This is a common value, another one is
`'\r'` i.e. 'carriage return', and in some cases the null byte '\0' is used.
+.. note::
+
+ For instruments that communicate over serial, you need to ensure you configure
+ the correct baud rate. The default baud rate is set to 9600, but you should
+ check your instrument's manual to verify the correct value for your use case.
+ If you wish to configure other serial instrument parameters, see
+ :ref:`api_resources` for a full list of attributes.
+
+You can configure PyVISA to communicate to your instrument using a different
+baud rate as follows:
+
+ >>> my_instrument.baud_rate = 57600
+
In an ideal world, this will work and you will be able to get an answer from
your instrument. If it does not, it means the settings are likely wrong (the
documentation does not always shine by its clarity). In the following we will
@@ -189,7 +203,7 @@ up a couple of paragraph).
.. note::
- It is possible to disable the use of teh termination character to detect
+ It is possible to disable the use of the termination character to detect
the end of an input message by setting |read_termination| to ``""``. Care
has to be taken for the case of serial instrument for which the method used
to determine the end of input is controlled by the |end_input| attribute
diff --git a/docs/source/introduction/event_handling.rst b/docs/source/introduction/event_handling.rst
index 3cd8598..743d07e 100644
--- a/docs/source/introduction/event_handling.rst
+++ b/docs/source/introduction/event_handling.rst
@@ -7,9 +7,10 @@ Event handling
VISA supports generating events on the instrument side usually when a register
change and handling then on the controller using two different mechanisms:
+
- storing the events in a queue
- calling a dedicated handler function registered for that purpose when the
-event occurs
+ event occurs
PyVISA supports using both mechanism and tries to provide a convenient interface
to both. Below we give a couple of example of how to use each mechanism (using
@@ -66,22 +67,22 @@ time and start the operation that will lead to the event. For the sake of that
example we are going to consider a Service Request event. Usually service request
can be enabled for a range of register state, the details depending on the
instrument. One useful case is to generate a service request when an operation
-is complete which is we are pretending to do here.
+is complete which is what we are pretending to do here.
Finally we wait for the event to occur and we specify a timeout of 1000ms to
-avoid waiting for ever. Once we received the event we disable event handling.
+avoid waiting forever. Once we receive the event we disable event handling.
Registering handlers for event
------------------------------
Rather than waiting for an event, it can sometimes be convenient to take immediate
-action when an event occurs, in which having the VISA library call directly a function
-can be useful. Let see how.
+action when an event occurs, in which case having the VISA library call a function
+directly can be useful. Let's see how.
.. note::
- One can enable event handling using both mechanisms (constants.EventMechanism.all)
+ One can enable event handling using both mechanisms (``constants.EventMechanism.all``)
.. code-block:: python
@@ -101,7 +102,7 @@ can be useful. Let see how.
# Type of event we want to be notified about
event_type = constants.EventType.service_request
# Mechanism by which we want to be notified
- event_mech = constants.EventMechanism.queue
+ event_mech = constants.EventMechanism.handler
wrapped = instr.wrap_handler(handle_event)
@@ -151,7 +152,7 @@ Next we install the handler and enable the event processing:
user_handle = instr.install_handler(event_type, wrapped, 42)
instr.enable_event(event_type, event_mech, None)
-When installing a handler one can optionally, specify a user handle that will be
+When installing a handler one can optionally specify a user handle that will be
passed to the handler. This handle can be used to identify which handler is called
when registering the same handler multiple times on the same resource. That value
may have to be converted by the backend. As a consequence the value passed to
diff --git a/docs/source/introduction/example.rst b/docs/source/introduction/example.rst
index 498421a..fca8f4c 100644
--- a/docs/source/introduction/example.rst
+++ b/docs/source/introduction/example.rst
@@ -37,14 +37,14 @@ SCPI and/or Keithley 2000 manual.
>>> keithley.write("trace:feed sense1; trace:feed:control next")
Okay, now the instrument is prepared to do the measurement. The next
-three lines make the instrument waiting for a trigger pulse, trigger
+three lines make the instrument wait for a trigger pulse, trigger
it, and wait until it sends a "service request"::
>>> keithley.write("initiate")
>>> keithley.assert_trigger()
>>> keithley.wait_for_srq()
-With sending the service request, the instrument tells us that the
+By sending the service request, the instrument tells us that the
measurement has been finished and that the results are ready for
transmission. We could read them with `keithley.query("trace:data?")`
however, then we'd get:
diff --git a/docs/source/introduction/resources.rst b/docs/source/introduction/resources.rst
index 09b0272..dd200fa 100644
--- a/docs/source/introduction/resources.rst
+++ b/docs/source/introduction/resources.rst
@@ -8,9 +8,9 @@ Resources
A resource represents an instrument, e.g. a measurement device. There are
multiple classes derived from resources representing the different available
types of resources (eg. GPIB, Serial). Each contains the particular set of
-attributes an methods that are available by the underlying device.
+attributes and methods that are available by the underlying device.
-You do not create this objects directly but they are returned by the
+You do not create these objects directly but they are returned by the
|open_resource| method of a |ResourceManager|. In general terms, there
are two main groups derived from |Resource|, |MessageBasedResource| and
|RegisterBasedResource|.
@@ -53,7 +53,7 @@ If the resource is closed, an exception will be raised:
timeout
~~~~~~~
-Very most VISA I/O operations may be performed with a timeout. If a timeout is
+Most VISA I/O operations may be performed with a timeout. If a timeout is
set, every operation that takes longer than the timeout is aborted and an
exception is raised. Timeouts are given per instrument in **milliseconds**.
@@ -71,12 +71,12 @@ is set to 25 seconds. To set an **infinite** timeout, set it to ``None`` or
del my_device.timeout
-To set it to **immediate**, set it to `0` or a negative value. (Actually, any
-value smaller than 1 is considered immediate)
-
Now every operation of the resource takes as long as it takes, even
indefinitely if necessary.
+To set it to **immediate**, set it to `0` or a negative value. (Actually, any
+value smaller than 1 is considered immediate)
+
Attributes of MessageBase resources
-----------------------------------
@@ -184,7 +184,7 @@ VISA attributes
In addition to the above mentioned attributes, you can access most of the VISA
attributes as defined in the visa standard on your resources through properties.
-Those properties will take for you of converting Python values to values VISA
+Those properties will take care of converting Python values to VISA
values and hence simplify their manipulations. Some of those attributes also
have lighter aliases that makes them easier to access as illustrated below:
@@ -204,7 +204,7 @@ have lighter aliases that makes them easier to access as illustrated below:
``attr`` command.
You can also manipulate the VISA attributes using |get_visa_attribute| and
-|set_visa_attribute|. However you will have use the proper values (as defined in
+|set_visa_attribute|. However you will have to use the proper values (as defined in
:mod:`pyvisa.constants`) both to access the attribute and to specify the value.
.. code:: python
diff --git a/docs/source/introduction/rvalues.rst b/docs/source/introduction/rvalues.rst
index cfb72de..35ae95e 100644
--- a/docs/source/introduction/rvalues.rst
+++ b/docs/source/introduction/rvalues.rst
@@ -105,15 +105,15 @@ termination character at the end of the message.
If you can read without any problem from your instrument, but cannot retrieve
the full message when using this method (VI_ERROR_CONN_LOST,
VI_ERROR_INV_SETUP, or Python simply crashes), try passing different values for
-``chunk_size``(the default is 20*1024). The underlying mechanism for this issue
+``chunk_size`` (the default is 20*1024). The underlying mechanism for this issue
is not clear but changing ``chunk_size`` has been used to work around it. Note
that using larger chunk sizes for large transfer may result in a speed up of
the transfer.
In some cases, the instrument may use a protocol that does not indicate how
-many bytes will be transferred. The Keithley 2000 for example always return the
-full buffer whose size is reported by the 'trace:points?' command. Since a
-binary block may contain the termination character, PyVISA need to know how
+many bytes will be transferred. The Keithley 2000 for example always returns the
+full buffer whose size is reported by the ``trace:points?`` command. Since a
+binary block may contain the termination character, PyVISA needs to know how
many bytes to expect. For those case, you can pass the expected number of
points using the ``data_points`` keyword argument. The number of bytes will be
inferred from the datatype of the block.
@@ -144,7 +144,7 @@ The separator can also be specified just like in ``query_ascii_values``.
>>> inst.write_ascii_values('WLISt:WAVeform:DATA somename,', values, converter='x', separator='$')
-You can provide a function to takes a iterable and returns an string.
+You can provide a function that takes a iterable and returns a string.
Default value for the separator is ``','`` (comma)
@@ -171,7 +171,7 @@ When things are not what they should be
PyVISA provides an easy way to transfer data from and to the device. The
methods described above work fine for 99% of the cases but there is always a
particular device that do not follow any of the standard protocols and is so
-different that cannot be adapted with the arguments provided above.
+different that it cannot be adapted with the arguments provided above.
In those cases, you need to get the data::
diff --git a/pyproject.toml b/pyproject.toml
index 5f713a4..b9195d1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,104 @@
+[project]
+name = "PyVISA"
+description = "Python VISA bindings for GPIB, RS232, TCPIP and USB instruments"
+readme = "README.rst"
+requires-python = ">=3.8"
+license = {file = "LICENSE"}
+authors = [
+ {name = "Torsten Bronger", email = "bronger@physik.rwth-aachen.de"},
+ {name = "Gregor Thalhammer"}
+]
+maintainers = [
+ {name = "Matthieu C. Dartiailh", email = "m.dartiailh@gmail.com"}
+]
+keywords = [
+ "VISA",
+ "GPIB",
+ "USB",
+ "serial",
+ "RS232",
+ "measurement",
+ "acquisition",
+]
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "Intended Audience :: Science/Research",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: Microsoft :: Windows",
+ "Operating System :: POSIX :: Linux",
+ "Operating System :: MacOS :: MacOS X",
+ "Programming Language :: Python",
+ "Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+]
+dependencies = [
+ "typing_extensions",
+ "importlib-metadata; python_version<'3.8'",
+]
+dynamic=["version"]
+
+
+[project.urls]
+homepage = "https://github.com/pyvisa/pyvisa"
+documentation = "https://pyvisa.readthedocs.io/en/latest/"
+repository = "https://github.com/pyvisa/pyvisa"
+changelog = "https://github.com/pyvisa/pyvisa/blob/main/CHANGES"
+
+[project.scripts]
+pyvisa-shell = "pyvisa.cmd_line_tools:visa_shell"
+pyvisa-info = "pyvisa.cmd_line_tools:visa_info"
+
[build-system]
-requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4.3"]
+requires = ["setuptools>=61.2", "wheel", "setuptools_scm[toml]>=3.4.3"]
build-backend = "setuptools.build_meta"
[tool.setuptools_scm]
+write_to = "pyvisa/version.py"
+write_to_template = """
+# This file is auto-generated by setuptools-scm do NOT edit it.
+
+from collections import namedtuple
+
+#: A namedtuple of the version info for the current release.
+_version_info = namedtuple("_version_info", "major minor micro status")
+
+parts = "{version}".split(".", 3)
+version_info = _version_info(
+ int(parts[0]),
+ int(parts[1]),
+ int(parts[2]),
+ parts[3] if len(parts) == 4 else "",
+)
+
+# Remove everything but the 'version_info' from this module.
+del namedtuple, _version_info, parts
+
+__version__ = "{version}"
+"""
+
+[tool.black]
+line-length = 88 # Enforce the default value
+
+[tool.pytest.ini_options]
+minversion = "6.0"
+
+[tool.mypy]
+follow_imports = "normal"
+strict_optional = true
+
+[[tool.mypy.overrides]]
+module = [
+ "pyvisa.thirdparty.*",
+]
+ignore_errors = true
+
+[tool.isort]
+profile = "black"
+skip = ["pyvisa/thirdparty/prettytable.py", "pyvisa/__init__.py"]
+
diff --git a/pyvisa/__init__.py b/pyvisa/__init__.py
index 506e665..cae3b98 100644
--- a/pyvisa/__init__.py
+++ b/pyvisa/__init__.py
@@ -3,17 +3,12 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
import logging
-import sys
-
-if sys.version_info >= (3, 8):
- from importlib.metadata import version, PackageNotFoundError
-else:
- from importlib_metadata import version, PackageNotFoundError # type: ignore
+from importlib.metadata import PackageNotFoundError, version
# Defined here since it is imported in other pyvisa modules
logger = logging.getLogger("pyvisa")
diff --git a/pyvisa/attributes.py b/pyvisa/attributes.py
index f74a99b..375fdd0 100644
--- a/pyvisa/attributes.py
+++ b/pyvisa/attributes.py
@@ -6,7 +6,7 @@ possible values for each attributes.
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -238,6 +238,38 @@ class EnumAttribute(Attribute):
return value
+class FlagAttribute(Attribute):
+ """Class for attributes with Flag values that map to a PyVISA Enum."""
+
+ #: Enum type with valid values.
+ enum_type: ClassVar[Type[enum.IntFlag]]
+
+ @classmethod
+ def redoc(cls) -> None:
+ """Add the enum member to the docstring."""
+ super(FlagAttribute, cls).redoc()
+ cls.__doc__ += "\n:type: :class:%s.%s" % (
+ cls.enum_type.__module__,
+ cls.enum_type.__name__,
+ )
+
+ def post_get(self, value: Any) -> enum.IntFlag:
+ """Convert the VISA value to the proper enum member."""
+ return self.enum_type(value)
+
+ def pre_set(self, value: enum.IntFlag) -> Any:
+ """Validate the value passed against the enum."""
+ # Python 3.8 raise if a non-Enum is used for value
+ try:
+ value = self.enum_type(value)
+ except ValueError:
+ raise ValueError(
+ "%r is an invalid value for attribute %s, "
+ "should be a %r" % (value, self.visa_name, self.enum_type)
+ )
+ return value
+
+
class IntAttribute(Attribute):
"""Class for attributes with integers values."""
@@ -357,8 +389,21 @@ class CharAttribute(Attribute):
# --- Session attributes ---------------------------------------------------------------
# Attributes are in the same order as in the constants.ResourceAttribute enum
-# VI_ATTR_RM_SESSION is not implemented as resource property,
-# use .resource_manager.session instead
+
+class AttrVI_ATTR_RM_SESSION(Attribute):
+ """Specifies the session of the Resource Manager used to open this session."""
+
+ resources = AllSessionTypes
+
+ py_name = ""
+
+ visa_name = "VI_ATTR_RM_SESSION"
+
+ visa_type = "ViSession"
+
+ default = NotAvailable
+
+ read, write, local = True, False, False
class AttrVI_ATTR_INTF_TYPE(EnumAttribute):
@@ -1260,7 +1305,7 @@ class AttrVI_ATTR_TCPIP_HISLIP_MAX_MESSAGE_KB(RangeAttribute):
visa_name = "VI_ATTR_TCPIP_HISLIP_MAX_MESSAGE_KB"
- visa_type = "ViUint32"
+ visa_type = "ViUInt32"
default = 1024
@@ -1612,7 +1657,7 @@ class AttrVI_ATTR_ASRL_STOP_BITS(EnumAttribute):
enum_type = constants.StopBits
-class AttrVI_ATTR_ASRL_FLOW_CNTRL(EnumAttribute):
+class AttrVI_ATTR_ASRL_FLOW_CNTRL(FlagAttribute):
"""Indicate the type of flow control used by the transfer mechanism."""
resources = [(constants.InterfaceType.asrl, "INSTR")]
@@ -1623,7 +1668,7 @@ class AttrVI_ATTR_ASRL_FLOW_CNTRL(EnumAttribute):
visa_type = "ViUInt16"
- default = constants.VI_ASRL_FLOW_NONE
+ default = constants.ControlFlow.none
read, write, local = True, True, False
@@ -2690,7 +2735,7 @@ class AttrVI_ATTR_FDC_MODE(RangeAttribute):
min_value, max_value, values = 0, 65535, None
-class AttrVVI_ATTR_FDC_GEN_SIGNAL_EN(BooleanAttribute):
+class AttrVI_ATTR_FDC_GEN_SIGNAL_EN(BooleanAttribute):
"""Fast Data Channel (FDC) signal enable."""
resources = [(constants.InterfaceType.vxi, "INSTR")]
@@ -2699,7 +2744,7 @@ class AttrVVI_ATTR_FDC_GEN_SIGNAL_EN(BooleanAttribute):
visa_name = "VI_ATTR_FDC_GEN_SIGNAL_EN"
- visa_type = "ViBool"
+ visa_type = "ViBoolean"
default = NotAvailable
@@ -2833,7 +2878,7 @@ class AttrVI_ATTR_MEM_SIZE(RangeAttribute):
visa_name = "VI_ATTR_MEM_SIZE"
- visa_type = "ViBusSize64" if constants.is_64bits else "ViUInt32"
+ visa_type = "ViBusSize64" if constants.is_64bits else "ViBusSize"
default = NotAvailable
@@ -3069,26 +3114,25 @@ class AttrVI_ATTR_VXI_TRIG_SUPPORT(RangeAttribute):
min_value, max_value, values = 0, 4294967295, None
-# GPIB-VXI is not supported
-# class AttrVI_ATTR_INTF_PARENT_NUM(RangeAttribute):
-# """This attribute shows the current state of the VXI/VME interrupt lines.
-# This is a bit vector with bits 0-6 corresponding to interrupt
-# lines 1-7.
-# """
+class AttrVI_ATTR_INTF_PARENT_NUM(RangeAttribute):
+ """This attribute shows the current state of the VXI/VME interrupt lines.
+ This is a bit vector with bits 0-6 corresponding to interrupt
+ lines 1-7.
+ """
-# resources = [(constants.InterfaceType.vxi, "BACKPLANE")]
+ resources = [(constants.InterfaceType.vxi, "BACKPLANE")]
-# py_name = ""
+ py_name = ""
-# visa_name = "VI_ATTR_INTF_PARENT_NUM"
+ visa_name = "VI_ATTR_INTF_PARENT_NUM"
-# visa_type = "ViUInt16"
+ visa_type = "ViUInt16"
-# default = NotAvailable
+ default = NotAvailable
-# read, write, local = True, False, False
+ read, write, local = True, False, False
-# min_value, max_value, values = 0, 65535, None
+ min_value, max_value, values = 0, 65535, None
class AttrVI_ATTR_VXI_DEV_CLASS(EnumAttribute):
@@ -3685,7 +3729,7 @@ class _AttrVI_ATTR_PXI_MEM_SIZE_BARX(RangeAttribute):
mod = sys.modules[__name__]
-for i in range(0, 5):
+for i in range(0, 6):
setattr(
mod,
f"AttrVI_ATTR_PXI_MEM_TYPE_BAR{i}",
@@ -3698,7 +3742,7 @@ for i in range(0, 5):
setattr(
mod,
- f"AttrVI_ATTR_PXI_MEM_TYPE_BAR{i}",
+ f"AttrVI_ATTR_PXI_MEM_BASE_BAR{i}",
type(
f"AttrVI_ATTR_PXI_MEM_BASE_BAR{i}",
(_AttrVI_ATTR_PXI_MEM_BASE_BARX,),
@@ -3708,7 +3752,7 @@ for i in range(0, 5):
setattr(
mod,
- f"AttrVI_ATTR_PXI_MEM_TYPE_BAR{i}",
+ f"AttrVI_ATTR_PXI_MEM_SIZE_BAR{i}",
type(
f"AttrVI_ATTR_PXI_MEM_SIZE_BAR{i}",
(_AttrVI_ATTR_PXI_MEM_SIZE_BARX,),
diff --git a/pyvisa/cmd_line_tools.py b/pyvisa/cmd_line_tools.py
index c43d7cf..f8218a4 100644
--- a/pyvisa/cmd_line_tools.py
+++ b/pyvisa/cmd_line_tools.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2019-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2019-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -51,8 +51,6 @@ def visa_main(command: Optional[str] = None) -> None:
from pyvisa import shell
shell.main("@" + args.backend if args.backend else "")
-
- shell.main("@" + args.backend if args.backend else "")
else:
raise ValueError(
f"Unknown command {args.command}. Valid values are: info and shell"
diff --git a/pyvisa/constants.py b/pyvisa/constants.py
index 888d1cb..83b4ed7 100644
--- a/pyvisa/constants.py
+++ b/pyvisa/constants.py
@@ -9,7 +9,7 @@ The module exports the values under the original, all-uppercase names.
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -18,7 +18,7 @@ import sys
from typing_extensions import Literal
-is_64bits = sys.maxsize > 2 ** 32
+is_64bits = sys.maxsize > 2**32
def _to_int(x: int) -> int:
@@ -178,7 +178,7 @@ VI_ATTR_MAX_QUEUE_LENGTH = 0x3FFF0005
VI_ATTR_USER_DATA_32 = 0x3FFF0007
VI_ATTR_USER_DATA_64 = 0x3FFF000A
VI_ATTR_USER_DATA = (
- VI_ATTR_USER_DATA_64 if is_64bits else VI_ATTR_USER_DATA_64
+ VI_ATTR_USER_DATA_64 if is_64bits else VI_ATTR_USER_DATA_32
)
VI_ATTR_FDC_CHNL = 0x3FFF000D
VI_ATTR_FDC_MODE = 0x3FFF000F
@@ -324,7 +324,7 @@ VI_ATTR_INTR_STATUS_ID = 0x3FFF4023
VI_ATTR_STATUS = 0x3FFF4025
VI_ATTR_RET_COUNT_32 = 0x3FFF4026
VI_ATTR_RET_COUNT_64 = 0x3FFF4028
-VI_ATTR_RET_COUNT = VI_ATTR_RET_COUNT_64 if is_64bits else VI_ATTR_RET_COUNT_64
+VI_ATTR_RET_COUNT = VI_ATTR_RET_COUNT_64 if is_64bits else VI_ATTR_RET_COUNT_32
VI_ATTR_BUFFER = 0x3FFF4027
VI_ATTR_RECV_INTR_LEVEL = 0x3FFF4041
VI_ATTR_OPER_NAME = 0xBFFF4042
@@ -816,6 +816,9 @@ class InterfaceType(enum.IntEnum):
#: Rohde and Schwarz Device via Passport
rsnrp = 33024
+ #: Lecroy VICP via passport
+ vicp = 36000 # FIXME
+
@enum.unique
class LineState(enum.IntEnum):
@@ -1077,7 +1080,7 @@ class WireMode(enum.IntEnum):
@enum.unique
-class ControlFlow(enum.IntEnum):
+class ControlFlow(enum.IntFlag):
"""Control flow for a serial resource."""
none = VI_ASRL_FLOW_NONE
diff --git a/pyvisa/ctwrapper/__init__.py b/pyvisa/ctwrapper/__init__.py
index 25a3ed2..451636c 100644
--- a/pyvisa/ctwrapper/__init__.py
+++ b/pyvisa/ctwrapper/__init__.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2019 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/ctwrapper/cthelper.py b/pyvisa/ctwrapper/cthelper.py
index 00b5ee7..1e06b9f 100644
--- a/pyvisa/ctwrapper/cthelper.py
+++ b/pyvisa/ctwrapper/cthelper.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/ctwrapper/functions.py b/pyvisa/ctwrapper/functions.py
index a04872b..b4afcf3 100644
--- a/pyvisa/ctwrapper/functions.py
+++ b/pyvisa/ctwrapper/functions.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/ctwrapper/highlevel.py b/pyvisa/ctwrapper/highlevel.py
index 79afcbd..6f8418d 100644
--- a/pyvisa/ctwrapper/highlevel.py
+++ b/pyvisa/ctwrapper/highlevel.py
@@ -3,13 +3,12 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
import ctypes
import logging
-import warnings
from collections import OrderedDict
from typing import (
Any,
@@ -26,7 +25,12 @@ from typing import (
from pyvisa import constants, errors, highlevel, logger, typing
-from ..util import LibraryPath, add_user_dll_extra_paths, read_user_library_path
+from ..util import (
+ DebugInfo,
+ LibraryPath,
+ add_user_dll_extra_paths,
+ read_user_library_path,
+)
from . import functions, types
from .cthelper import Library, find_library
@@ -105,11 +109,11 @@ class IVIVisaLibrary(highlevel.VisaLibraryBase):
)
@classmethod
- def get_debug_info(cls) -> Dict[str, Union[str, Dict[str, Any]]]:
+ def get_debug_info(cls) -> DebugInfo:
"""Return a list of lines with backend info."""
from pyvisa import __version__
- d: Dict[str, Union[str, Dict[str, Any]]] = OrderedDict()
+ d: Dict[str, Any] = OrderedDict()
d["Version"] = "%s (bundled with PyVISA)" % __version__
paths = cls.get_library_paths()
@@ -128,12 +132,12 @@ class IVIVisaLibrary(highlevel.VisaLibraryBase):
)
nfo["Impl. Version"] = str(
lib.get_attribute(
- sess, constants.ResourceAttribute.resource_manufacturer_name
+ sess, constants.ResourceAttribute.resource_impl_version
)[0]
)
nfo["Spec. Version"] = str(
lib.get_attribute(
- sess, constants.ResourceAttribute.resource_manufacturer_name
+ sess, constants.ResourceAttribute.resource_spec_version
)[0]
)
lib.close(sess)
@@ -356,41 +360,3 @@ class IVIVisaLibrary(highlevel.VisaLibraryBase):
return buffer
return None
-
-
-class NIVisaLibrary(IVIVisaLibrary): # pragma: no cover
- """Deprecated name for IVIVisaLibrary.
-
- This class will be removed in 1.12
-
- """
-
- @staticmethod
- def get_library_paths() -> Tuple[LibraryPath, ...]:
- """Return a tuple of possible library paths."""
- warnings.warn(
- "NIVisaLibrary is deprecated and will be removed in 1.12. "
- "Use IVIVisaLibrary instead.",
- FutureWarning,
- )
- return IVIVisaLibrary.get_library_paths()
-
- @classmethod
- def get_debug_info(cls) -> Dict[str, Union[str, Dict[str, Any]]]:
- """Return a list of lines with backend info."""
- warnings.warn(
- "NIVisaLibrary is deprecated and will be removed in 1.12. "
- "Use IVIVisaLibrary instead.",
- FutureWarning,
- )
- return IVIVisaLibrary.get_debug_info()
-
- def __new__( # type: ignore
- cls: Type["NIVisaLibrary"], library_path: str = ""
- ) -> highlevel.VisaLibraryBase:
- warnings.warn(
- "NIVisaLibrary is deprecated and will be removed in 1.12. "
- "Use IVIVisaLibrary instead.",
- FutureWarning,
- )
- return IVIVisaLibrary.__new__(cls, library_path)
diff --git a/pyvisa/ctwrapper/types.py b/pyvisa/ctwrapper/types.py
index 67087e2..59d6e70 100644
--- a/pyvisa/ctwrapper/types.py
+++ b/pyvisa/ctwrapper/types.py
@@ -8,7 +8,7 @@ All data types that are defined by VPP-4.3.2.
The module exports all data types including the pointer and array types. This
means "ViUInt32" and such.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -110,6 +110,7 @@ ViBusAddress, ViPBusAddress = _type_pair(ViUInt32)
ViBusAddress64, ViPBusAddress64 = _type_pair(ViUInt64)
ViBusSize = ViUInt32
+ViBusSize64 = ViUInt64
ViAttrState, ViPAttrState = _type_pair(ViUInt32)
diff --git a/pyvisa/errors.py b/pyvisa/errors.py
index a10269c..d271a4d 100644
--- a/pyvisa/errors.py
+++ b/pyvisa/errors.py
@@ -3,11 +3,10 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
-import warnings
from typing import Any, Tuple
from . import typing, util
@@ -737,49 +736,3 @@ class LibraryError(OSError, Error):
s += " bitness: %s\n" % visalib.bitness
return cls("Error while accessing %s: %s" % (filename, s))
-
-
-# TODO remove when removing return handler
-def _args_to_str(args: tuple, kwargs: dict) -> str: # pragma: no cover
- return "args=%s, kwargs=%s" % (args, kwargs)
-
-
-def return_handler(module_logger, first_is_session=True): # pragma: no cover
- """Decorate a VISA library function returning an error code."""
- warnings.warn("return_handler will be removed in 1.12", FutureWarning)
-
- def _outer(visa_library_method):
- def _inner(self, session, *args, **kwargs):
-
- ret_value = visa_library_method(*args, **kwargs)
- module_logger.debug(
- "%s%s -> %r",
- visa_library_method.__name__,
- _args_to_str(args, kwargs),
- ret_value,
- )
-
- try:
- ret_value = StatusCode(ret_value)
- except ValueError:
- pass
-
- if first_is_session:
- self._last_status = ret_value
- self._last_status_in_session[session] = ret_value
-
- if ret_value < 0:
- raise VisaIOError(ret_value)
-
- if ret_value in self.issue_warning_on:
- if (
- session
- and ret_value not in self._ignore_warning_in_session[session]
- ):
- module_logger.warn(VisaIOWarning(ret_value), stacklevel=2)
-
- return ret_value
-
- return _inner
-
- return _outer
diff --git a/pyvisa/events.py b/pyvisa/events.py
index 995cb4d..c1dc810 100644
--- a/pyvisa/events.py
+++ b/pyvisa/events.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2020-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/highlevel.py b/pyvisa/highlevel.py
index 045387f..0fcfefe 100644
--- a/pyvisa/highlevel.py
+++ b/pyvisa/highlevel.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -50,7 +50,7 @@ from .typing import (
VISARMSession,
VISASession,
)
-from .util import LibraryPath
+from .util import DebugInfo, LibraryPath
if TYPE_CHECKING:
from .resources import Resource # pragma: no cover
@@ -89,7 +89,7 @@ class VisaLibraryBase(object):
to the underlying devices providing Pythonic wrappers to VISA functions. But not all
derived class must/will implement all methods. Even if methods are expected to return
the status code they are expected to raise the appropriate exception when an error
- ocurred since this is more Pythonic.
+ occurred since this is more Pythonic.
The default VisaLibrary class is :class:`pyvisa.ctwrapper.highlevel.IVIVisaLibrary`,
which implements a ctypes wrapper around the IVI-VISA library.
@@ -127,7 +127,7 @@ class VisaLibraryBase(object):
#: Maps session handle to warnings to ignore.
_ignore_warning_in_session: Dict[int, set]
- #: Extra inforatoion used for logging errors
+ #: Extra information used for logging errors
_logging_extra: Dict[str, str]
#: Contains all installed event handlers.
@@ -210,7 +210,7 @@ class VisaLibraryBase(object):
return ()
@staticmethod
- def get_debug_info() -> Union[Iterable[str], Dict[str, Union[str, Dict[str, Any]]]]:
+ def get_debug_info() -> DebugInfo:
"""Override to return an iterable of lines with the backend debug details."""
return ["Does not provide debug info"]
@@ -865,7 +865,7 @@ class VisaLibraryBase(object):
----------
session : VISASession
Unique logical identifier to a session.
- event_type : constans.EventType
+ event_type : constants.EventType
Logical event identifier.
mechanism : constants.EventMechanism
Specifies event handling mechanisms to be discarded.
@@ -2806,26 +2806,14 @@ class PyVISAModule(ModuleType):
def get_wrapper_class(backend_name: str) -> Type[VisaLibraryBase]:
- """Return the WRAPPER_CLASS for a given backend.
-
- backend_name == 'ni' is used for backwards compatibility
- and will be removed in 1.12.
-
- """
+ """Return the WRAPPER_CLASS for a given backend."""
try:
return _WRAPPERS[backend_name]
except KeyError:
- if backend_name == "ivi" or backend_name == "ni":
+ if backend_name == "ivi":
from .ctwrapper import IVIVisaLibrary
_WRAPPERS["ivi"] = IVIVisaLibrary
- if backend_name == "ni":
- warnings.warn(
- "@ni backend name is deprecated and will be "
- "removed in 1.12. Use @ivi instead. "
- "Check the documentation for details",
- FutureWarning,
- )
return IVIVisaLibrary
pkg: PyVISAModule
@@ -2834,20 +2822,7 @@ def get_wrapper_class(backend_name: str) -> Type[VisaLibraryBase]:
_WRAPPERS[backend_name] = cls = pkg.WRAPPER_CLASS
return cls
except ImportError:
- try:
- pkg = cast(PyVISAModule, import_module("pyvisa-" + backend_name))
- _WRAPPERS[backend_name] = cls = pkg.WRAPPER_CLASS
- warnings.warn(
- "Backends packages should use an _ rather than a - ."
- "Project can/should keep using a - (like pytest plugins)."
- "Support for backends with - will be removed in 1.12",
- FutureWarning,
- )
- return cls
- except ImportError:
- raise ValueError(
- "Wrapper not found: No package named pyvisa_%s" % backend_name
- )
+ raise ValueError("Wrapper not found: No package named pyvisa_%s" % backend_name)
def _get_default_wrapper() -> str:
@@ -2933,7 +2908,7 @@ def open_visa_library(specification: str = "") -> VisaLibraryBase:
class ResourceManager(object):
- """VISA Resource Manager. """
+ """VISA Resource Manager."""
#: Maps (Interface Type, Resource Class) to Python class encapsulating that resource.
_resource_classes: ClassVar[
@@ -2963,10 +2938,10 @@ class ResourceManager(object):
Parameters
----------
- interface_type : constants.InterfaceTyp
+ interface_type : constants.InterfaceType
Interface type for which to use the provided class.
resource_class : str
- Resource class (INSTR, INTFC, ...) for which to use teh povided class.
+ Resource class (INSTR, INTFC, ...) for which to use the provided class.
python_class : Type[Resource]
Subclass of ``Resource`` to use when opening a resource matching the
specified interface type and resource class.
@@ -2980,7 +2955,9 @@ class ResourceManager(object):
# If the class already has this attribute, it means that a parent class
# was registered first. We need to copy the current set and extend it.
- attrs = copy.copy(getattr(python_class, "visa_attributes_classes", set()))
+ attrs: Set[Type[attributes.Attribute]] = copy.copy(
+ getattr(python_class, "visa_attributes_classes", set())
+ )
for attr in chain(
attributes.AttributesPerResource[(interface_type, resource_class)],
@@ -3266,7 +3243,6 @@ class ResourceManager(object):
Subclass of Resource matching the resource.
"""
-
if resource_pyclass is None:
info = self.resource_info(resource_name, extended=True)
@@ -3284,76 +3260,32 @@ class ResourceManager(object):
"There is no class defined for %r. Using Resource",
(info.interface_type, info.resource_class),
)
+ if hasattr(self.visalib, "open_resource"):
+ res = self.visalib.open_resource( # type: ignore
+ resource_name, access_mode, open_timeout, resource_pyclass, **kwargs
+ )
+ else:
+ res = resource_pyclass(self, resource_name)
+ for key in kwargs.keys():
+ try:
+ getattr(res, key)
+ present = True
+ except AttributeError:
+ present = False
+ except errors.InvalidSession:
+ present = True
- res = resource_pyclass(self, resource_name)
- for key in kwargs.keys():
- try:
- getattr(res, key)
- present = True
- except AttributeError:
- present = False
- except errors.InvalidSession:
- present = True
-
- if not present:
- raise ValueError(
- "%r is not a valid attribute for type %s"
- % (key, res.__class__.__name__)
- )
+ if not present:
+ raise ValueError(
+ "%r is not a valid attribute for type %s"
+ % (key, res.__class__.__name__)
+ )
- res.open(access_mode, open_timeout)
+ res.open(access_mode, open_timeout)
- for key, value in kwargs.items():
- setattr(res, key, value)
+ for key, value in kwargs.items():
+ setattr(res, key, value)
self._created_resources.add(res)
return res
-
- def get_instrument(
- self,
- resource_name: str,
- access_mode: constants.AccessModes = constants.AccessModes.no_lock,
- open_timeout: int = constants.VI_TMO_IMMEDIATE,
- resource_pyclass: Type["Resource"] = None,
- **kwargs: Any,
- ) -> "Resource":
- """Return an instrument for the resource name.
-
- .. warning::
- get_instrument is deprecated and will be removed in 1.12,
- use open_resource instead."
-
- Parameters
- ----------
- resource_name : str
- Name or alias of the resource to open.
- access_mode : constants.AccessModes, optional
- Specifies the mode by which the resource is to be accessed,
- by default constants.AccessModes.no_lock
- open_timeout : int, optional
- If the ``access_mode`` parameter requests a lock, then this
- parameter specifies the absolute time period (in milliseconds) that
- the resource waits to get unlocked before this operation returns an
- error, by default constants.VI_TMO_IMMEDIATE.
- resource_pyclass : Optional[Type[Resource]], optional
- Resource Python class to use to instantiate the Resource.
- Defaults to None: select based on the resource name.
- kwargs : Any
- Keyword arguments to be used to change instrument attributes
- after construction.
-
- Returns
- -------
- Resource
- Subclass of Resource matching the resource.
-
- """
- warnings.warn(
- "get_instrument is deprecated and will be removed in "
- "1.12, use open_resource instead.",
- FutureWarning,
- )
- return self.open_resource(
- resource_name, access_mode, open_timeout, resource_pyclass, **kwargs
- )
diff --git a/pyvisa/resources/__init__.py b/pyvisa/resources/__init__.py
index ce5f53b..d674b37 100644
--- a/pyvisa/resources/__init__.py
+++ b/pyvisa/resources/__init__.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/resources/firewire.py b/pyvisa/resources/firewire.py
index 48ea465..8ea03f8 100644
--- a/pyvisa/resources/firewire.py
+++ b/pyvisa/resources/firewire.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/resources/gpib.py b/pyvisa/resources/gpib.py
index 65958ad..686e895 100644
--- a/pyvisa/resources/gpib.py
+++ b/pyvisa/resources/gpib.py
@@ -3,11 +3,10 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
-import warnings
from time import perf_counter
from typing import Tuple
@@ -31,107 +30,6 @@ class _GPIBMixin(ControlRenMixin):
constants.LineState
] = attributes.AttrVI_ATTR_GPIB_REN_STATE()
- def send_command(self, data: bytes) -> Tuple[int, constants.StatusCode]:
- """Write GPIB command bytes on the bus.
-
- Corresponds to viGpibCommand function of the VISA library.
-
- Parameters
- ----------
- data : bytes
- Command to write.
-
- Returns
- -------
- int
- Number of bytes written
- constants.StatusCode
- Return value of the library call.
-
- """
- if not isinstance(self, GPIBInterface):
- warnings.warn(
- FutureWarning(
- "`send_command` is only supported on GPIB::INTFC resources "
- "and the methods will be removed in PyVISA 1.12"
- )
- )
- return self.visalib.gpib_command(self.session, data)
-
- def control_atn(self, mode: constants.ATNLineOperation) -> constants.StatusCode:
- """Specifies the state of the ATN line and the local active controller state.
-
- Corresponds to viGpibControlATN function of the VISA library.
-
- Parameters
- ----------
- mode : constants.ATNLineOperation
- Specifies the state of the ATN line and optionally the local active
- controller state.
-
- Returns
- -------
- constants.StatusCode
- Return value of the library call.
-
- """
- if not isinstance(self, GPIBInterface):
- warnings.warn(
- FutureWarning(
- "`control_atn` is only supported on GPIB::INTFC resources "
- "and the methods will be removed in PyVISA 1.12"
- )
- )
- return self.visalib.gpib_control_atn(self.session, mode)
-
- def pass_control(
- self, primary_address: int, secondary_address: int
- ) -> constants.StatusCode:
- """Tell a GPIB device to become controller in charge (CIC).
-
- Corresponds to viGpibPassControl function of the VISA library.
-
- Parameters
- ----------
- primary_address : int
- Primary address of the GPIB device to which you want to pass control.
- secondary_address : int
- Secondary address of the targeted GPIB device.
- If the targeted device does not have a secondary address,
- this parameter should contain the value Constants.NO_SEC_ADDR.
-
- Returns
- -------
- constants.StatusCode
- Return value of the library call.
-
- """
- if not isinstance(self, GPIBInterface):
- warnings.warn(
- FutureWarning(
- "`pass_control` is only supported on GPIB::INTFC resources "
- "and the methods will be removed in PyVISA 1.12"
- )
- )
- return self.visalib.gpib_pass_control(
- self.session, primary_address, secondary_address
- )
-
- def send_ifc(self) -> constants.StatusCode:
- """Pulse the interface clear line (IFC) for at least 100 microseconds.
-
- Corresponds to viGpibSendIFC function of the VISA library.
-
- """
- if not isinstance(self, GPIBInterface):
- warnings.warn(
- FutureWarning(
- "`send_ifc` is only supported on GPIB::INTFC resources "
- "and the methods will be removed in PyVISA 1.12"
- )
- )
- return self.visalib.gpib_send_ifc(self.session)
-
@Resource.register(constants.InterfaceType.gpib, "INSTR")
class GPIBInstrument(_GPIBMixin, MessageBasedResource):
@@ -225,6 +123,79 @@ class GPIBInterface(_GPIBMixin, MessageBasedResource):
constants.LineState
] = attributes.AttrVI_ATTR_GPIB_ADDR_STATE()
+ def send_command(self, data: bytes) -> Tuple[int, constants.StatusCode]:
+ """Write GPIB command bytes on the bus.
+
+ Corresponds to viGpibCommand function of the VISA library.
+
+ Parameters
+ ----------
+ data : bytes
+ Command to write.
+
+ Returns
+ -------
+ int
+ Number of bytes written
+ constants.StatusCode
+ Return value of the library call.
+
+ """
+ return self.visalib.gpib_command(self.session, data)
+
+ def control_atn(self, mode: constants.ATNLineOperation) -> constants.StatusCode:
+ """Specifies the state of the ATN line and the local active controller state.
+
+ Corresponds to viGpibControlATN function of the VISA library.
+
+ Parameters
+ ----------
+ mode : constants.ATNLineOperation
+ Specifies the state of the ATN line and optionally the local active
+ controller state.
+
+ Returns
+ -------
+ constants.StatusCode
+ Return value of the library call.
+
+ """
+ return self.visalib.gpib_control_atn(self.session, mode)
+
+ def pass_control(
+ self, primary_address: int, secondary_address: int
+ ) -> constants.StatusCode:
+ """Tell a GPIB device to become controller in charge (CIC).
+
+ Corresponds to viGpibPassControl function of the VISA library.
+
+ Parameters
+ ----------
+ primary_address : int
+ Primary address of the GPIB device to which you want to pass control.
+ secondary_address : int
+ Secondary address of the targeted GPIB device.
+ If the targeted device does not have a secondary address,
+ this parameter should contain the value Constants.NO_SEC_ADDR.
+
+ Returns
+ -------
+ constants.StatusCode
+ Return value of the library call.
+
+ """
+ return self.visalib.gpib_pass_control(
+ self.session, primary_address, secondary_address
+ )
+
+ def send_ifc(self) -> constants.StatusCode:
+ """Pulse the interface clear line (IFC) for at least 100 microseconds.
+
+ Corresponds to viGpibSendIFC function of the VISA library.
+
+ """
+ return self.visalib.gpib_send_ifc(self.session)
+
def group_execute_trigger(
self, *resources: GPIBInstrument
) -> Tuple[int, constants.StatusCode]:
diff --git a/pyvisa/resources/messagebased.py b/pyvisa/resources/messagebased.py
index 092886a..63f5a6a 100644
--- a/pyvisa/resources/messagebased.py
+++ b/pyvisa/resources/messagebased.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -20,7 +20,7 @@ from .resource import Resource
class ControlRenMixin(object):
- """Common control_ren method of some messaged based resources. """
+ """Common control_ren method of some messaged based resources."""
#: Will be present when used as a mixin with Resource
visalib: VisaLibraryBase
@@ -468,7 +468,7 @@ class MessageBasedResource(Resource):
----------
termination : Optional[str], optional
Alternative character termination to use. If None, the value of
- write_termination is used. Defaults to None.
+ read_termination is used. Defaults to None.
encoding : Optional[str], optional
Alternative encoding to use to turn bytes into str. If None, the
value of encoding is used. Defaults to None.
@@ -537,7 +537,7 @@ class MessageBasedResource(Resource):
container: Union[Type, Callable[[Iterable], Sequence]] = list,
header_fmt: util.BINARY_HEADERS = "ieee",
expect_termination: bool = True,
- data_points: int = 0,
+ data_points: int = -1,
chunk_size: Optional[int] = None,
) -> Sequence[Union[int, float]]:
"""Read values from the device in binary format returning an iterable
@@ -581,7 +581,7 @@ class MessageBasedResource(Resource):
offset, data_length = util.parse_hp_block_header(block, is_big_endian)
elif header_fmt == "empty":
offset = 0
- data_length = 0
+ data_length = -1
else:
raise ValueError(
"Invalid header format. Valid options are 'ieee'," " 'empty', 'hp'"
@@ -589,7 +589,9 @@ class MessageBasedResource(Resource):
# Allow to support instrument such as the Keithley 2000 that do not
# report the length of the block
- data_length = data_length or data_points * struct.calcsize(datatype)
+ data_length = (
+ data_length if data_length >= 0 else data_points * struct.calcsize(datatype)
+ )
expected_length = offset + data_length
@@ -597,10 +599,12 @@ class MessageBasedResource(Resource):
expected_length += len(self._read_termination)
# Read all the data if we know what to expect.
- if data_length != 0:
+ if data_length > 0:
block.extend(
self.read_bytes(expected_length - len(block), chunk_size=chunk_size)
)
+ elif data_length == 0:
+ pass
else:
raise ValueError(
"The length of the data to receive could not be "
diff --git a/pyvisa/resources/pxi.py b/pyvisa/resources/pxi.py
index fe9e743..f918263 100644
--- a/pyvisa/resources/pxi.py
+++ b/pyvisa/resources/pxi.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/resources/registerbased.py b/pyvisa/resources/registerbased.py
index 089bc57..97f5477 100644
--- a/pyvisa/resources/registerbased.py
+++ b/pyvisa/resources/registerbased.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/resources/resource.py b/pyvisa/resources/resource.py
index b6ddd6a..a6fdf19 100644
--- a/pyvisa/resources/resource.py
+++ b/pyvisa/resources/resource.py
@@ -3,13 +3,12 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
import contextlib
import time
-import warnings
from functools import update_wrapper
from typing import (
Any,
@@ -66,24 +65,6 @@ class WaitResponse:
self._visalib = visalib
self.timed_out = timed_out
- @property
- def event_type(self) -> Optional[constants.EventType]:
- warnings.warn(
- "event_type is deprecated and will be removed in 1.12. "
- "Use the event object instead.",
- FutureWarning,
- )
- return self._event_type
-
- @property
- def context(self) -> Optional[VISAEventContext]:
- warnings.warn(
- "context is deprecated and will be removed in 1.12. "
- "Use the event object instead to access the event attributes.",
- FutureWarning,
- )
- return self._context
-
def __del__(self) -> None:
if self.event._context is not None:
try:
@@ -227,6 +208,10 @@ class Resource(object):
#: VISA attributes require the resource to be opened in order to get accessed.
#: Please have a look at the attributes definition for more details
+ # VI_ATTR_RM_SESSION is not implemented as resource property,
+ # use .resource_manager.session instead
+ # resource_manager_session: Attribute[int] = attributes.AttrVI_ATTR_RM_SESSION()
+
#: Interface type of the given session.
interface_type: Attribute[
constants.InterfaceType
diff --git a/pyvisa/resources/serial.py b/pyvisa/resources/serial.py
index 55ff795..8bbbc7d 100644
--- a/pyvisa/resources/serial.py
+++ b/pyvisa/resources/serial.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -21,19 +21,22 @@ class SerialInstrument(MessageBasedResource):
"""
- #: Baud rate of the interface.
+ #: Baud rate of the interface. The default value is 9600.
baud_rate: Attribute[int] = attributes.AttrVI_ATTR_ASRL_BAUD()
- #: Number of data bits contained in each frame (from 5 to 8).
+ #: Number of data bits contained in each frame (from 5 to 8). The default value is 8.
data_bits: Attribute[int] = attributes.AttrVI_ATTR_ASRL_DATA_BITS()
- #: Parity used with every frame transmitted and received.
+ #: Parity used with every frame transmitted and received. The default value is
+ #: `constants.Parity.none` (VI_ASRL_PAR_NONE).
parity: Attribute[constants.Parity] = attributes.AttrVI_ATTR_ASRL_PARITY()
- #: Number of stop bits used to indicate the end of a frame.
+ #: Number of stop bits used to indicate the end of a frame. The default value is
+ #: `constants.StopBits.one` (VI_ASRL_STOP_ONE).
stop_bits: Attribute[constants.StopBits] = attributes.AttrVI_ATTR_ASRL_STOP_BITS()
- #: Indicates the type of flow control used by the transfer mechanism.
+ #: Indicates the type of flow control used by the transfer mechanism. The default value
+ #: is `constants.ControlFlow.none` (VI_ASRL_FLOW_NONE).
flow_control: Attribute[
constants.ControlFlow
] = attributes.AttrVI_ATTR_ASRL_FLOW_CNTRL()
@@ -41,35 +44,42 @@ class SerialInstrument(MessageBasedResource):
#: Number of bytes available in the low- level I/O receive buffer.
bytes_in_buffer: Attribute[int] = attributes.AttrVI_ATTR_ASRL_AVAIL_NUM()
- #: If set to True, NUL characters are discarded.
+ #: If set to True, NUL characters are discarded. The default is False.
discard_null: Attribute[bool] = attributes.AttrVI_ATTR_ASRL_DISCARD_NULL()
- #: Manually control transmission.
+ #: Manually control transmission. The default value is True.
allow_transmit: Attribute[bool] = attributes.AttrVI_ATTR_ASRL_ALLOW_TRANSMIT()
- #: Method used to terminate read operations.
+ #: Method used to terminate read operations. The default value is
+ #: `constants.SerialTermination.termination_char` (VI_ASRL_END_TERMCHAR).
end_input: Attribute[
constants.SerialTermination
] = attributes.AttrVI_ATTR_ASRL_END_IN()
- #: Method used to terminate write operations.
+ #: Method used to terminate write operations. The default value is
+ #: `constants.SerialTermination.none` (VI_ASRL_END_NONE) and terminates
+ #: when all requested data is transferred or when an error occurs.
end_output: Attribute[
constants.SerialTermination
] = attributes.AttrVI_ATTR_ASRL_END_OUT()
- #: Duration (in milliseconds) of the break signal.
+ #: Duration (in milliseconds) of the break signal. The default value is 250.
break_length: Attribute[int] = attributes.AttrVI_ATTR_ASRL_BREAK_LEN()
- #: Manually control the assertion state of the break signal.
+ #: Manually control the assertion state of the break signal. The default state is
+ #: `constants.LineState.unasserted` (VI_STATE_UNASSERTED).
break_state: Attribute[
constants.LineState
] = attributes.AttrVI_ATTR_ASRL_BREAK_STATE()
#: Character to be used to replace incoming characters that arrive with errors.
+ #: The default character is '\0'.
replace_char: Attribute[str] = attributes.AttrVI_ATTR_ASRL_REPLACE_CHAR()
#: XOFF character used for XON/XOFF flow control (both directions).
+ #: The default character is '0x13'.
xoff_char: Attribute[str] = attributes.AttrVI_ATTR_ASRL_XOFF_CHAR()
#: XON character used for XON/XOFF flow control (both directions).
+ #: The default character is '0x11'.
xon_char: Attribute[str] = attributes.AttrVI_ATTR_ASRL_XON_CHAR()
diff --git a/pyvisa/resources/tcpip.py b/pyvisa/resources/tcpip.py
index 6c67dd3..8c0cb15 100644
--- a/pyvisa/resources/tcpip.py
+++ b/pyvisa/resources/tcpip.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -27,6 +27,18 @@ class TCPIPInstrument(ControlRenMixin, MessageBasedResource):
pass
+@Resource.register(constants.InterfaceType.vicp, "INSTR")
+class VICPInstrument(ControlRenMixin, MessageBasedResource):
+ """Communicates with to devices of type VICP::host address[::INSTR]
+
+ Do not instantiate directly, use
+ :meth:`pyvisa.highlevel.ResourceManager.open_resource`.
+
+ """
+
+ pass
+
+
@Resource.register(constants.InterfaceType.tcpip, "SOCKET")
class TCPIPSocket(MessageBasedResource):
"""Communicates with to devices of type TCPIP::host address::port::SOCKET
diff --git a/pyvisa/resources/usb.py b/pyvisa/resources/usb.py
index 3d538d7..b913474 100644
--- a/pyvisa/resources/usb.py
+++ b/pyvisa/resources/usb.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -118,7 +118,7 @@ class USBInstrument(ControlRenMixin, USBCommon):
index : int
wIndex parameter of the setup stage of a USB control transfer.
This is usually the index of the interface or endpoint.
- data : str
+ data : bytes
The data buffer that sends the data in the optional data stage of
the control transfer.
diff --git a/pyvisa/resources/vxi.py b/pyvisa/resources/vxi.py
index ac2d2e4..f3b9698 100644
--- a/pyvisa/resources/vxi.py
+++ b/pyvisa/resources/vxi.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/rname.py b/pyvisa/rname.py
index 9918a41..c0095e3 100644
--- a/pyvisa/rname.py
+++ b/pyvisa/rname.py
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
"""Functions and classes to parse and assemble resource name.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
import contextlib
import re
-from collections import defaultdict
+from collections import OrderedDict, defaultdict
from dataclasses import dataclass, field, fields
from typing import (
TYPE_CHECKING,
@@ -50,7 +50,7 @@ class InvalidResourceName(ValueError):
@classmethod
def bad_syntax(
- cls, syntax: str, resource_name: str, ex: Exception = None
+ cls, syntax: str, resource_name: str, ex: Optional[Exception] = None
) -> "InvalidResourceName":
"""Build an exception when the resource name cannot be parsed."""
if ex:
@@ -64,7 +64,9 @@ class InvalidResourceName(ValueError):
@classmethod
def subclass_notfound(
- cls, interface_type_resource_class: Tuple[str, str], resource_name: str = None
+ cls,
+ interface_type_resource_class: Tuple[str, str],
+ resource_name: Optional[str] = None,
) -> "InvalidResourceName":
"""Build an exception when no parser has been registered for a pair."""
@@ -77,7 +79,7 @@ class InvalidResourceName(ValueError):
@classmethod
def rc_notfound(
- cls, interface_type: str, resource_name: str = None
+ cls, interface_type: str, resource_name: Optional[str] = None
) -> "InvalidResourceName":
"""Build an exception when no resource class is provided and no default is found."""
@@ -98,23 +100,28 @@ T = TypeVar("T", bound=Type["ResourceName"])
def register_subclass(cls: T) -> T:
- """Register a subclass for a given interface type and resource class."""
+ """Register a subclass for a given interface type and resource class.
+
+ Fields with a default value of None will be fully omitted from the resource
+ string when formatted.
+
+ """
# Assemble the format string based on the resource parts
- fmt = cls.interface_type
+ fmt = OrderedDict([("interface_type", cls.interface_type)])
syntax = cls.interface_type
for ndx, f in enumerate(fields(cls)):
sep = "::" if ndx else ""
- fmt += sep + "{0.%s}" % f.name
+ fmt[f.name] = sep + "{0}"
- if not f.default:
+ if f.default == "":
syntax += sep + f.name.replace("_", " ")
else:
syntax += "[" + sep + f.name.replace("_", " ") + "]"
- fmt += "::" + cls.resource_class
+ fmt["resource_class"] = "::" + cls.resource_class
if not cls.is_rc_optional:
syntax += "::" + cls.resource_class
@@ -155,7 +162,7 @@ class ResourceName:
is_rc_optional: ClassVar[bool] = False
#: Formatting string for canonical
- _canonical_fmt: str = field(init=False)
+ _canonical_fmt: Dict[str, str] = field(init=False)
#: VISA syntax for resource
_visa_syntax: str = field(init=False)
@@ -169,7 +176,7 @@ class ResourceName:
def __post_init__(self):
# Ensure that all mandatory arguments have been passed
for f in fields(self):
- if not getattr(self, f.name):
+ if getattr(self, f.name) == "":
raise TypeError(f.name + " is a required parameter")
self._fields = tuple(f.name for f in fields(self))
@@ -296,7 +303,7 @@ class ResourceName:
# The rest of the parts are consumed when mandatory elements are required.
while len(pending) < len(rp):
k, rp = rp[0], rp[1:]
- if not k.default:
+ if k.default == "":
# This is impossible as far as I can tell for currently implemented
# resource names
if not pending:
@@ -315,7 +322,12 @@ class ResourceName:
return cls(**kwargs)
def __str__(self):
- return self._canonical_fmt.format(self)
+ s = ""
+ for part, form in self._canonical_fmt.items():
+ value = getattr(self, part, None)
+ if value is not None:
+ s += form.format(value)
+ return s
# Build subclasses for each resource
@@ -340,7 +352,8 @@ class GPIBInstr(ResourceName):
#: Secondary address of the device to connect to
# Reference for the GPIB secondary address
# https://www.mathworks.com/help/instrument/secondaryaddress.html
- secondary_address: str = "0"
+ # NOTE: a secondary address of 0 is not the same as no secondary address.
+ secondary_address: Optional[str] = None
interface_type: ClassVar[str] = "GPIB"
resource_class: ClassVar[str] = "INSTR"
@@ -406,6 +419,27 @@ class TCPIPInstr(ResourceName):
is_rc_optional: ClassVar[bool] = True
+@register_subclass
+@dataclass
+class VICPInstr(ResourceName):
+ """VICP INSTR
+
+ The syntax is:
+ VICP[board]::host address[::INSTR]
+
+ """
+
+ #: Board to use.
+ board: str = "0"
+
+ #: Host address of the device (IPv4 or host name)
+ host_address: str = ""
+
+ interface_type: ClassVar[str] = "VICP"
+ resource_class: ClassVar[str] = "INSTR"
+ is_rc_optional: ClassVar[bool] = True
+
+
@register_subclass
@dataclass
class TCPIPSocket(ResourceName):
@@ -757,7 +791,11 @@ class _AttrGetter:
if not isinstance(self.parsed, GPIBInstr):
raise self.raise_missing_attr(item)
else:
- return int(self.parsed.secondary_address)
+ return (
+ int(self.parsed.secondary_address)
+ if self.parsed.secondary_address is not None
+ else constants.VI_NO_SEC_ADDR
+ )
elif item == "VI_ATTR_PXI_CHASSIS":
if not isinstance(self.parsed, PXIBackplane):
raise self.raise_missing_attr(item)
diff --git a/pyvisa/shell.py b/pyvisa/shell.py
index 3f7e3c8..a6dea8f 100644
--- a/pyvisa/shell.py
+++ b/pyvisa/shell.py
@@ -4,7 +4,7 @@
This file is taken from the Lantz Project.
-:copyright: (c) 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: (c) 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
@@ -122,7 +122,7 @@ class VisaShell(cmd.Cmd):
self.prompt = self.default_prompt
def do_query(self, args):
- """Query resource in use: query *IDN? """
+ """Query resource in use: query *IDN?"""
if not self.current:
print('There are no resources in use. Use the command "open".')
@@ -146,7 +146,7 @@ class VisaShell(cmd.Cmd):
print(e)
def do_write(self, args):
- """Send to the resource in use: send *IDN? """
+ """Send to the resource in use: send *IDN?"""
if not self.current:
print('There are no resources in use. Use the command "open".')
@@ -328,7 +328,7 @@ class VisaShell(cmd.Cmd):
if not args:
try:
- charmap = {u"\r": "CR", u"\n": "LF", u"\r\n": "CRLF", u"\0": "NUL"}
+ charmap = {"\r": "CR", "\n": "LF", "\r\n": "CRLF", "\0": "NUL"}
chr = self.current.read_termination
if chr in charmap:
chr = charmap[chr]
@@ -349,10 +349,10 @@ class VisaShell(cmd.Cmd):
)
else:
charmap = {
- "CR": u"\r",
- "LF": u"\n",
- "CRLF": u"\r\n",
- "NUL": u"\0",
+ "CR": "\r",
+ "LF": "\n",
+ "CRLF": "\r\n",
+ "NUL": "\0",
"None": None,
}
chr = args[0]
diff --git a/pyvisa/testsuite/fake-extensions/pyvisa_test_open.py b/pyvisa/testsuite/fake-extensions/pyvisa_test_open.py
new file mode 100644
index 0000000..6d010db
--- /dev/null
+++ b/pyvisa/testsuite/fake-extensions/pyvisa_test_open.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+"""
+
+"""
+from pyvisa import constants
+from pyvisa.highlevel import VisaLibraryBase
+from pyvisa.util import LibraryPath
+
+
+class FakeResource:
+ def close(self):
+ pass
+
+
+class FalseVISALib(VisaLibraryBase):
+ pass
+
+ @staticmethod
+ def get_library_paths():
+ return (LibraryPath("unset"),)
+
+ def _init(self):
+ pass
+
+ def open_resource(self, *args, **kwargs):
+ self.open_resource_called = True
+ return FakeResource()
+
+ def open_default_resource_manager(self):
+ return 1, constants.StatusCode.success
+
+ def close(self, session):
+ pass
+
+
+WRAPPER_CLASS = FalseVISALib
diff --git a/pyvisa/testsuite/keysight_assisted_tests/__init__.py b/pyvisa/testsuite/keysight_assisted_tests/__init__.py
index ae7d075..0993b3e 100644
--- a/pyvisa/testsuite/keysight_assisted_tests/__init__.py
+++ b/pyvisa/testsuite/keysight_assisted_tests/__init__.py
@@ -22,22 +22,35 @@ import types
import pytest
+_KEYSIGHT_AVAILABLE = "PYVISA_KEYSIGHT_VIRTUAL_INSTR" in os.environ
+
require_virtual_instr = pytest.mark.skipif(
- "PYVISA_KEYSIGHT_VIRTUAL_INSTR" not in os.environ,
- reason="Requires the Keysight virtual instrument. Run on PyVISA " "buildbot.",
+ not _KEYSIGHT_AVAILABLE,
+ reason="Requires the Keysight virtual instrument. Run on PyVISA buildbot.",
)
-RESOURCE_ADDRESSES = {
- # "GPIB::INSTR": "GPIB::19::INSTR",
- # "USB::INSTR": "USB::",
- "TCPIP::INSTR": "TCPIP::127.0.0.1::INSTR", # ie localhost
- "TCPIP::SOCKET": "TCPIP::127.0.0.1::5025::SOCKET",
-}
+# We are testing locally with only TCPIP resources on the loop back address
+if _KEYSIGHT_AVAILABLE and os.environ["PYVISA_KEYSIGHT_VIRTUAL_INSTR"] == "0":
+ RESOURCE_ADDRESSES = {
+ "TCPIP::INSTR": "TCPIP::127.0.0.1::INSTR",
+ "TCPIP::SOCKET": "TCPIP::127.0.0.1::5025::SOCKET",
+ }
+
+ ALIASES = {}
+
+# We are running on the bot with all supported resources.
+else:
+ RESOURCE_ADDRESSES = {
+ # "USB::INSTR": "USB::",
+ "TCPIP::INSTR": "TCPIP::192.168.0.2::INSTR",
+ "TCPIP::SOCKET": "TCPIP::192.168.0.2::5025::SOCKET",
+ # "GPIB::INSTR": "GPIB::19::INSTR",
+ }
-ALIASES = {
- "TCPIP::127.0.0.1::INSTR": "tcpip",
-}
+ ALIASES = {
+ "TCPIP::192.168.0.2::INSTR": "tcpip",
+ }
# Even a deepcopy is not a true copy of a function.
diff --git a/pyvisa/testsuite/keysight_assisted_tests/messagebased_resource_utils.py b/pyvisa/testsuite/keysight_assisted_tests/messagebased_resource_utils.py
index 53df4a2..219d30a 100644
--- a/pyvisa/testsuite/keysight_assisted_tests/messagebased_resource_utils.py
+++ b/pyvisa/testsuite/keysight_assisted_tests/messagebased_resource_utils.py
@@ -6,6 +6,8 @@ import ctypes
import gc
import logging
import time
+from types import ModuleType
+from typing import Optional
import pytest
@@ -19,8 +21,11 @@ from .resource_utils import (
ResourceTestCase,
)
+np: Optional[ModuleType]
try:
- import numpy as np # type: ignore
+ import numpy
+
+ np = numpy
except ImportError:
np = None
@@ -298,7 +303,8 @@ class MessagebasedResourceTestCase(ResourceTestCase):
assert self.instr.read() == "\r1;2;3;4;5\r"
@pytest.mark.parametrize(
- "hfmt, prefix", zip(("ieee", "hp", "empty"), (b"#212", b"#A\x0c\x00", b""))
+ "hfmt, prefix",
+ list(zip(("ieee", "hp", "empty"), (b"#212", b"#A\x0c\x00", b""))),
)
def test_write_binary_values(self, hfmt, prefix):
"""Test writing binary data."""
@@ -350,11 +356,13 @@ class MessagebasedResourceTestCase(ResourceTestCase):
with pytest.raises(ValueError):
self.instr.write_binary_values("", values, "h", header_fmt="zxz")
- def test_read_ascii_values(self):
+ # Without and with trailing comma
+ @pytest.mark.parametrize("msg", ["1,2,3,4,5", "1,2,3,4,5,"])
+ def test_read_ascii_values(self, msg):
"""Test reading ascii values."""
# Standard separator
self.instr.write("RECEIVE")
- self.instr.write("1,2,3,4,5")
+ self.instr.write(msg)
self.instr.write("SEND")
values = self.instr.read_ascii_values()
assert type(values[0]) is float
@@ -362,7 +370,7 @@ class MessagebasedResourceTestCase(ResourceTestCase):
# Non standard separator and termination
self.instr.write("RECEIVE")
- self.instr.write("1;2;3;4;5")
+ self.instr.write(msg.replace(",", ";"))
tic = time.time()
values = self.instr.query_ascii_values(
"SEND", converter="d", separator=";", delay=0.5
@@ -374,7 +382,7 @@ class MessagebasedResourceTestCase(ResourceTestCase):
# Numpy container
if np:
self.instr.write("RECEIVE")
- self.instr.write("1,2,3,4,5")
+ self.instr.write(msg)
self.instr.write("SEND")
values = self.instr.read_ascii_values(container=np.array)
expected = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
@@ -459,12 +467,10 @@ class MessagebasedResourceTestCase(ResourceTestCase):
# Not sure how to test this
@pytest.mark.skip
def test_handling_malformed_binary(self):
- """"""
+ """ """
pass
- @pytest.mark.parametrize(
- "hfmt, header", zip(("ieee", "hp", "empty"), ("#10", "#A\x00\x00", ""))
- )
+ @pytest.mark.parametrize("hfmt, header", list(zip(("ieee", "empty"), ("#0", ""))))
def test_read_binary_values_unreported_length(self, hfmt, header):
"""Test reading binary data."""
self.instr.read_termination = "\r"
@@ -526,6 +532,38 @@ class MessagebasedResourceTestCase(ResourceTestCase):
chunk_size=6,
)
+ def test_read_binary_values_empty(self):
+ """Test reading binary data."""
+ self.instr.write("RECEIVE")
+ self.instr.write("#10")
+ self.instr.write("SEND")
+ new = self.instr.read_binary_values(
+ datatype="h",
+ is_big_endian=False,
+ header_fmt="ieee",
+ expect_termination=True,
+ chunk_size=6,
+ )
+ assert not new
+
+ if np:
+ self.instr.write("RECEIVE")
+ self.instr.write(
+ "#10",
+ termination="\n",
+ )
+ new = self.instr.query_binary_values(
+ "SEND",
+ datatype="h",
+ header_fmt="ieee",
+ is_big_endian=True,
+ expect_termination=False,
+ chunk_size=6,
+ container=np.array if np else list,
+ )
+
+ assert not new.size if np else not new
+
def test_delay_in_query_ascii(self):
"""Test handling of the delay argument in query_ascii_values."""
# Test using the instrument wide delay
@@ -560,7 +598,7 @@ class MessagebasedResourceTestCase(ResourceTestCase):
def test_instrument_wide_delay_in_query_binary(self):
"""Test handling delay in query_ascii_values."""
- header = "#10"
+ header = "#0"
data = [1, 2, 3328, 3, 4, 5]
# Test using the instrument wide delay
self.instr.query_delay = 1.0
@@ -585,7 +623,7 @@ class MessagebasedResourceTestCase(ResourceTestCase):
def test_delay_args_in_query_binary(self):
"""Test handling of the delay argument in query_ascii_values."""
- header = "#10"
+ header = "#0"
data = [1, 2, 3328, 3, 4, 5]
self.instr.query_delay = 0.0
self.instr.write("RECEIVE")
@@ -610,7 +648,7 @@ class MessagebasedResourceTestCase(ResourceTestCase):
def test_no_delay_args_in_query_binary(self):
"""Test handling of the delay argument in query_ascii_values."""
- header = "#10"
+ header = "#0"
data = [1, 2, 3328, 3, 4, 5]
self.instr.query_delay = 1.0
self.instr.write("RECEIVE")
@@ -776,11 +814,6 @@ class EventAwareMessagebasedResourceTestCaseMixin(EventAwareResourceTestCaseMixi
assert response.event.event_type == EventType.service_request
assert self.instr.read() == "1"
- with pytest.warns(FutureWarning):
- response.event_type
- with pytest.warns(FutureWarning):
- response.context
-
def test_managing_visa_handler(self):
"""Test using visa handlers."""
diff --git a/pyvisa/testsuite/keysight_assisted_tests/test_resource_manager.py b/pyvisa/testsuite/keysight_assisted_tests/test_resource_manager.py
index a61175c..dda8100 100644
--- a/pyvisa/testsuite/keysight_assisted_tests/test_resource_manager.py
+++ b/pyvisa/testsuite/keysight_assisted_tests/test_resource_manager.py
@@ -50,6 +50,8 @@ class TestResourceManager:
self.rm.session
assert self.rm.visalib.resource_manager is None
+ # This test is flaky
+ @pytest.mark.skip
def test_cleanup_on_del(self, caplog):
"""Test that deleting the rm does clean the VISA session"""
# The test seems to assert what it should even though the coverage report
@@ -218,12 +220,6 @@ class TestResourceManager:
assert len(self.rm.list_opened_resources()) == 0
- def test_get_instrument(self):
- """Check that we get the expected deprecation warning."""
- rname = list(RESOURCE_ADDRESSES.values())[0]
- with pytest.warns(FutureWarning):
- self.rm.get_instrument(rname)
-
@require_virtual_instr
class TestResourceParsing(BaseTestCase):
diff --git a/pyvisa/testsuite/keysight_assisted_tests/test_tcpip_resources.py b/pyvisa/testsuite/keysight_assisted_tests/test_tcpip_resources.py
index ab44334..23705b6 100644
--- a/pyvisa/testsuite/keysight_assisted_tests/test_tcpip_resources.py
+++ b/pyvisa/testsuite/keysight_assisted_tests/test_tcpip_resources.py
@@ -103,6 +103,9 @@ class TestTCPIPSocket(MessagebasedResourceTestCase):
MessagebasedResourceTestCase.test_read_binary_values_unreported_length
)
)
+ test_read_binary_values_empty = pytest.mark.xfail(
+ copy_func(MessagebasedResourceTestCase.test_read_binary_values_empty)
+ )
test_delay_in_query_ascii = pytest.mark.xfail(
copy_func(MessagebasedResourceTestCase.test_delay_in_query_ascii)
)
diff --git a/pyvisa/testsuite/test_cmd_line_tools.py b/pyvisa/testsuite/test_cmd_line_tools.py
index a3e61f3..349da30 100644
--- a/pyvisa/testsuite/test_cmd_line_tools.py
+++ b/pyvisa/testsuite/test_cmd_line_tools.py
@@ -15,31 +15,6 @@ from . import BaseTestCase, require_visa_lib
class TestCmdLineTools(BaseTestCase):
"""Test the cmd line tools functions and scripts."""
- @require_visa_lib
- def test_visa_main(self):
- """Test the visa scripts.
-
- The script is deprecated and will be removed, when it does this
- should be removed too.
-
- """
- result = run(
- ["python", "-m", "visa", "info"], stdout=PIPE, universal_newlines=True
- )
- details = util.system_details_to_str(util.get_system_details())
- print(result.stdout.strip())
- print()
- print(details.strip())
- # Path difference can lead to subtle differences in the backends
- # compare only the first lines.
- assert (
- result.stdout.strip().split("\n")[:16] == details.strip().split("\n")[:16]
- )
-
- with Popen(["python", "-m", "visa", "shell"], stdin=PIPE, stdout=PIPE) as p:
- stdout, _ = p.communicate(b"exit")
- assert b"Welcome to the VISA shell" in stdout
-
def test_visa_main_argument_handling(self):
"""Test we reject invalid values in visa_main."""
from pyvisa.cmd_line_tools import visa_main
@@ -56,10 +31,8 @@ class TestCmdLineTools(BaseTestCase):
result = run("pyvisa-info", stdout=PIPE, universal_newlines=True)
details = util.system_details_to_str(util.get_system_details())
# Path difference can lead to subtle differences in the backends
- # compare only the first lines.
- assert (
- result.stdout.strip().split("\n")[:16] == details.strip().split("\n")[:16]
- )
+ # and Python path so compare only the first lines.
+ assert result.stdout.strip().split("\n")[:5] == details.strip().split("\n")[:5]
# TODO test backend selection: this is not easy at all to assert
@require_visa_lib
@@ -67,4 +40,4 @@ class TestCmdLineTools(BaseTestCase):
"""Test the visa shell function."""
with Popen(["pyvisa-shell"], stdin=PIPE, stdout=PIPE) as p:
stdout, stderr = p.communicate(b"exit")
- assert b"Welcome to the VISA shell" in stdout
+ assert stdout.count(b"Welcome to the VISA shell") == 1
diff --git a/pyvisa/testsuite/test_constants.py b/pyvisa/testsuite/test_constants.py
index 739abcd..792700a 100644
--- a/pyvisa/testsuite/test_constants.py
+++ b/pyvisa/testsuite/test_constants.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2019-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2019-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/testsuite/test_event.py b/pyvisa/testsuite/test_event.py
index fa521cb..4a4def5 100644
--- a/pyvisa/testsuite/test_event.py
+++ b/pyvisa/testsuite/test_event.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2019-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2019-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/testsuite/test_highlevel.py b/pyvisa/testsuite/test_highlevel.py
index 4eb0d58..489f39c 100644
--- a/pyvisa/testsuite/test_highlevel.py
+++ b/pyvisa/testsuite/test_highlevel.py
@@ -5,10 +5,11 @@
import logging
import os
import sys
+from importlib import import_module
import pytest
-from pyvisa import constants, highlevel, resources, rname
+from pyvisa import ResourceManager, constants, highlevel, resources, rname
from pyvisa.ctwrapper import IVIVisaLibrary
from . import BaseTestCase
@@ -130,8 +131,7 @@ class TestHighlevel(BaseTestCase):
"""Test retrieving a wrapper class."""
highlevel._WRAPPERS.clear()
- with pytest.warns(FutureWarning):
- highlevel.get_wrapper_class("ni")
+ highlevel.get_wrapper_class("ivi")
assert "ivi" in highlevel._WRAPPERS
path = os.path.join(os.path.dirname(__file__), "fake-extensions")
@@ -247,3 +247,22 @@ class TestHighlevel(BaseTestCase):
def test_base_get_debug_info(self):
"""Test the base class implementation of get_debug_info."""
assert len(highlevel.VisaLibraryBase.get_debug_info()) == 1
+
+ def test_open_resource_attr(self, caplog):
+ """Test handling errors when trying to open a Visa library."""
+ highlevel._WRAPPERS.clear()
+
+ path = os.path.join(os.path.dirname(__file__), "fake-extensions")
+ sys.path.append(path)
+ try:
+ pkg = import_module("pyvisa_test_open")
+ highlevel.get_wrapper_class("test_open")
+ rm = ResourceManager("@test_open")
+ assert rm is not None
+ finally:
+ sys.path.remove(path)
+
+ instr = rm.open_resource("TCPIP::192.168.0.1::INSTR")
+ assert isinstance(instr, pkg.FakeResource)
+ assert rm.visalib.open_resource_called
+ rm.close()
diff --git a/pyvisa/testsuite/test_rname.py b/pyvisa/testsuite/test_rname.py
index 66b7488..a179939 100644
--- a/pyvisa/testsuite/test_rname.py
+++ b/pyvisa/testsuite/test_rname.py
@@ -97,7 +97,7 @@ class TestResourceName(BaseTestCase):
# Test handling less than required parts
with pytest.raises(rname.InvalidResourceName) as e:
- rname.ResourceName.from_string("GPIB::INSTR")
+ rname.ResourceName.from_string("GPIB")
assert "not enough parts" in e.exconly()
# Test handling more than possible parts
@@ -212,8 +212,8 @@ class TestParsers(BaseTestCase):
resource_class="INSTR",
board="0",
primary_address="1",
- secondary_address="0",
- canonical_resource_name="GPIB0::1::0::INSTR",
+ secondary_address=None,
+ canonical_resource_name="GPIB0::1::INSTR",
)
self._parse_test(
@@ -222,8 +222,8 @@ class TestParsers(BaseTestCase):
resource_class="INSTR",
board="1",
primary_address="1",
- secondary_address="0",
- canonical_resource_name="GPIB1::1::0::INSTR",
+ secondary_address=None,
+ canonical_resource_name="GPIB1::1::INSTR",
)
self._parse_test(
@@ -232,8 +232,8 @@ class TestParsers(BaseTestCase):
resource_class="INSTR",
board="1",
primary_address="1",
- secondary_address="0",
- canonical_resource_name="GPIB1::1::0::INSTR",
+ secondary_address=None,
+ canonical_resource_name="GPIB1::1::INSTR",
)
def test_gpib_intf(self):
@@ -295,6 +295,44 @@ class TestParsers(BaseTestCase):
canonical_resource_name="TCPIP3::1.2.3.4::inst3::INSTR",
)
+ def test_vicp_intr(self):
+
+ self._parse_test(
+ "VICP::192.168.134.102",
+ interface_type="VICP",
+ resource_class="INSTR",
+ host_address="192.168.134.102",
+ board="0",
+ canonical_resource_name="VICP0::192.168.134.102::INSTR",
+ )
+
+ self._parse_test(
+ "VICP::dev.company.com::INSTR",
+ interface_type="VICP",
+ resource_class="INSTR",
+ host_address="dev.company.com",
+ board="0",
+ canonical_resource_name="VICP0::dev.company.com::INSTR",
+ )
+
+ self._parse_test(
+ "VICP3::dev.company.com::INSTR",
+ interface_type="VICP",
+ resource_class="INSTR",
+ host_address="dev.company.com",
+ board="3",
+ canonical_resource_name="VICP3::dev.company.com::INSTR",
+ )
+
+ self._parse_test(
+ "VICP3::1.2.3.4::INSTR",
+ interface_type="VICP",
+ resource_class="INSTR",
+ host_address="1.2.3.4",
+ board="3",
+ canonical_resource_name="VICP3::1.2.3.4::INSTR",
+ )
+
def test_tcpip_socket(self):
self._parse_test(
"TCPIP::1.2.3.4::999::SOCKET",
@@ -439,7 +477,7 @@ class TestFilters(BaseTestCase):
"USB0::0x1112::0x2223::0x1234::0::INSTR",
"TCPIP0::192.168.0.1::inst1::INSTR",
"TCPIP0::localhost::10001::SOCKET",
- "GPIB9::7::65535::INSTR",
+ "GPIB9::7::1::INSTR",
"ASRL11::INSTR",
"ASRL2::INSTR",
"GPIB::INTFC",
@@ -503,7 +541,10 @@ class TestFilters(BaseTestCase):
self._test_filter2('?*{VI_ATTR_TCPIP_DEVICE_NAME == "inst1"}', 5)
self._test_filter2("?*{VI_ATTR_TCPIP_PORT == 10001}", 6)
self._test_filter2("?*{VI_ATTR_GPIB_PRIMARY_ADDR == 8}", 0)
- self._test_filter2("?*{VI_ATTR_GPIB_SECONDARY_ADDR == 0}", 0)
+ self._test_filter2("?*{VI_ATTR_GPIB_SECONDARY_ADDR == 1}", 7)
+ self._test_filter2(
+ "?*{VI_ATTR_GPIB_SECONDARY_ADDR == %d}" % constants.VI_NO_SEC_ADDR, 0
+ )
self._test_filter2("?*{VI_ATTR_PXI_CHASSIS == 1}", 11)
self._test_filter2("?*{VI_ATTR_MAINFRAME_LA == 1}", 13, 14)
self._test_filter2("?*{VI_ATTR_MAINFRAME_LA == 1 && VI_test == 1}", 13, 14)
diff --git a/pyvisa/testsuite/test_util.py b/pyvisa/testsuite/test_util.py
index 84fa3ef..37d9bc5 100644
--- a/pyvisa/testsuite/test_util.py
+++ b/pyvisa/testsuite/test_util.py
@@ -10,9 +10,12 @@ import struct
import subprocess
import sys
import tempfile
+import unittest
from configparser import ConfigParser
from functools import partial
from io import StringIO
+from types import ModuleType
+from typing import Optional
import pytest
@@ -20,8 +23,11 @@ from pyvisa import highlevel, util
from pyvisa.ctwrapper import IVIVisaLibrary
from pyvisa.testsuite import BaseTestCase
+np: Optional[ModuleType]
try:
- import numpy as np # type: ignore
+ import numpy
+
+ np = numpy
except ImportError:
np = None
@@ -38,7 +44,9 @@ class TestConfigFile(BaseTestCase):
os.path.join(os.path.expanduser("~"), ".pyvisarc"),
]
):
- self.skipTest(".pyvisarc file exists cannot properly test in this case")
+ raise unittest.SkipTest(
+ ".pyvisarc file exists cannot properly test in this case"
+ )
self.temp_dir = tempfile.TemporaryDirectory()
os.makedirs(os.path.join(self.temp_dir.name, "share", "pyvisa"))
self.config_path = os.path.join(
@@ -158,7 +166,7 @@ class TestConfigFile(BaseTestCase):
class TestParser(BaseTestCase):
def test_parse_binary(self):
s = (
- b"#A@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xde\x8b<@\xde\x8b<@"
+ b"#0@\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<"
)
@@ -189,6 +197,10 @@ class TestParser(BaseTestCase):
for a, b in zip(p, e):
assert a == pytest.approx(b)
+ # Test handling zero length block
+ p = util.from_ieee_block(b"#10" + s[2:], datatype="f", is_big_endian=False)
+ assert not p
+
p = util.from_hp_block(
b"#A\x0e\x00" + s[2:],
datatype="f",
@@ -203,7 +215,8 @@ class TestParser(BaseTestCase):
for fmt in "d":
msg = "block=%s, fmt=%s"
msg = msg % ("ascii", fmt)
- tb = lambda values: util.to_ascii_block(values, fmt, ",")
+ # Test handling the case of a trailing comma
+ tb = lambda values: util.to_ascii_block(values, fmt, ",") + ","
fb = lambda block, cont: util.from_ascii_block(block, fmt, ",", cont)
self.round_trip_block_conversion(values, tb, fb, msg)
@@ -281,10 +294,10 @@ class TestParser(BaseTestCase):
(util.to_ieee_block, util.to_hp_block),
(util.from_ieee_block, util.from_hp_block),
):
- for fmt in "sp":
- block = tb(values, datatype="s")
- print(block)
- rt = fb(block, datatype="s", container=bytes)
+ for fmt in "sbB":
+ block = tb(values, datatype=fmt)
+ print(fmt, block)
+ rt = fb(block, datatype=fmt, container=bytes)
assert values == rt
def test_malformed_binary_block_header(self):
@@ -374,7 +387,7 @@ class TestParser(BaseTestCase):
block = block[:2] + b"0" * header_length + block[2 + header_length :]
else:
block = block[:2] + b"\x00\x00\x00\x00" + block[2 + 4 :]
- assert fb(block, "h", False, list) == values
+ assert not fb(block, "h", False, list)
def test_handling_malformed_binary(self):
containers = (list, tuple) + ((np.array, np.ndarray) if np else ())
diff --git a/pyvisa/thirdparty/__init__.py b/pyvisa/thirdparty/__init__.py
index 7ede0ca..d80ed56 100644
--- a/pyvisa/thirdparty/__init__.py
+++ b/pyvisa/thirdparty/__init__.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/thirdparty/prettytable.py b/pyvisa/thirdparty/prettytable.py
index 6fe1591..214229c 100644
--- a/pyvisa/thirdparty/prettytable.py
+++ b/pyvisa/thirdparty/prettytable.py
@@ -41,8 +41,6 @@ import sys
import textwrap
import unicodedata
-import pkg_resources
-
__version__ = "0.7.3.dev.43cdb910a6fbee396e1fceb76a7775fa7314ee1d"
py3k = sys.version_info[0] >= 3
if py3k:
@@ -51,15 +49,16 @@ if py3k:
itermap = map
iterzip = zip
uni_chr = chr
- from html.parser import HTMLParser
from html import escape
+ from html.parser import HTMLParser
else:
itermap = itertools.imap
iterzip = itertools.izip
uni_chr = unichr # noqa: F821
- from HTMLParser import HTMLParser
from cgi import escape
+ from HTMLParser import HTMLParser
+
# hrule styles
FRAME = 0
ALL = 1
@@ -939,7 +938,7 @@ class PrettyTable(object):
@property
def oldsortslice(self):
- """ oldsortslice - Slice rows before sorting in the "old style" """
+ """oldsortslice - Slice rows before sorting in the "old style" """
return self._oldsortslice
@oldsortslice.setter
diff --git a/pyvisa/typing.py b/pyvisa/typing.py
index 69c9aeb..48e81b2 100644
--- a/pyvisa/typing.py
+++ b/pyvisa/typing.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2020-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
diff --git a/pyvisa/util.py b/pyvisa/util.py
index ab8a699..f40cc0b 100644
--- a/pyvisa/util.py
+++ b/pyvisa/util.py
@@ -3,7 +3,7 @@
This file is part of PyVISA.
-:copyright: 2014-2020 by PyVISA Authors, see AUTHORS for more details.
+:copyright: 2014-2022 by PyVISA Authors, see AUTHORS for more details.
:license: MIT, see LICENSE for more details.
"""
@@ -18,6 +18,7 @@ import subprocess
import sys
import warnings
from collections import OrderedDict
+from types import ModuleType
from typing import (
Any,
Callable,
@@ -36,8 +37,11 @@ from typing_extensions import Literal
from . import constants, logger
+np: Optional[ModuleType]
try:
- import numpy as np # type: ignore
+ import numpy
+
+ np = numpy
except ImportError:
np = None
@@ -228,40 +232,6 @@ def cleanup_timeout(timeout: Optional[Union[int, float]]) -> int:
return timeout
-def warn_for_invalid_kwargs(keyw, allowed_keys): # pragma: no cover
- warnings.warn("warn_for_invalid_kwargs will be removed in 1.12", FutureWarning)
- for key in keyw.keys():
- if key not in allowed_keys:
- warnings.warn('Keyword argument "%s" unknown' % key, stacklevel=3)
-
-
-def filter_kwargs(keyw, selected_keys): # pragma: no cover
- warnings.warn("warn_for_invalid_kwargs will be removed in 1.12", FutureWarning)
- result = {}
- for key, value in keyw.items():
- if key in selected_keys:
- result[key] = value
- return result
-
-
-def split_kwargs(keyw, self_keys, parent_keys, warn=True): # pragma: no cover
- warnings.warn("warn_for_invalid_kwargs will be removed in 1.12", FutureWarning)
- self_kwargs = dict()
- parent_kwargs = dict()
- self_keys = set(self_keys)
- parent_keys = set(parent_keys)
- all_keys = self_keys | parent_keys
- for key, value in keyw.items():
- if warn and key not in all_keys:
- warnings.warn('Keyword argument "%s" unknown' % key, stacklevel=3)
- if key in self_keys:
- self_kwargs[key] = value
- if key in parent_keys:
- parent_kwargs[key] = value
-
- return self_kwargs, parent_kwargs
-
-
_converters: Dict[str, Callable[[str], Any]] = {
"s": str,
"b": functools.partial(int, base=2),
@@ -332,6 +302,7 @@ def from_ascii_block(
and isinstance(separator, str)
and converter in _np_converters
):
+ assert np # for typing
return np.fromstring(ascii_data, _np_converters[converter], sep=separator)
if isinstance(converter, str):
@@ -346,6 +317,8 @@ def from_ascii_block(
data: Iterable[str]
if isinstance(separator, str):
data = ascii_data.split(separator)
+ if not data[-1]:
+ data = data[:-1]
else:
data = separator(ascii_data)
@@ -467,7 +440,7 @@ def parse_ieee_block_header(
else:
# #0DATA
# 012
- data_length = 0
+ data_length = -1
return offset, data_length
@@ -475,7 +448,7 @@ def parse_ieee_block_header(
def parse_hp_block_header(
block: Union[bytes, bytearray],
is_big_endian: bool,
- length_before_block: int = None,
+ length_before_block: Optional[int] = None,
raise_on_late_block: bool = False,
) -> Tuple[int, int]:
"""Parse the header of a HP block.
@@ -577,7 +550,7 @@ def from_ieee_block(
# If the data length is not reported takes all the data and do not make
# any assumption about the termination character
- if data_length == 0:
+ if data_length == -1:
data_length = len(block) - offset
if len(block) < offset + data_length:
@@ -627,11 +600,6 @@ def from_hp_block(
"""
offset, data_length = parse_hp_block_header(block, is_big_endian)
- # If the data length is not reported takes all the data and do not make
- # any assumption about the termination character
- if data_length == 0:
- data_length = len(block) - offset
-
if len(block) < offset + data_length:
raise ValueError(
"Binary data is incomplete. The header states %d data"
@@ -687,6 +655,7 @@ def from_binary_block(
endianess = ">" if is_big_endian else "<"
if _use_numpy_routines(container):
+ assert np # for typing
return np.frombuffer(block, endianess + datatype, array_length, offset)
fullfmt = "%s%d%s" % (endianess, array_length, datatype)
@@ -703,7 +672,7 @@ def from_binary_block(
def to_binary_block(
- iterable: Sequence[Union[int, float]],
+ iterable: Union[bytes, bytearray, Sequence[Union[int, float]]],
header: Union[str, bytes],
datatype: BINARY_DATATYPES,
is_big_endian: bool,
@@ -727,12 +696,27 @@ def to_binary_block(
Binary block of data preceded by the specified header
"""
- array_length = len(iterable)
+ if isinstance(header, str):
+ header = header.encode("ascii")
+
+ if isinstance(iterable, (bytes, bytearray)):
+ if datatype not in "sbB":
+ warnings.warn(
+ "Using the formats 's', 'p', 'b' or 'B' is more efficient when "
+ "directly writing bytes",
+ UserWarning,
+ )
+ else:
+ return header + iterable
+
endianess = ">" if is_big_endian else "<"
- fullfmt = "%s%d%s" % (endianess, array_length, datatype)
- if isinstance(header, str):
- header = bytes(header, "ascii")
+ if _use_numpy_routines(type(iterable)):
+ assert np and isinstance(iterable, np.ndarray) # For typing
+ return header + iterable.astype(endianess + datatype).tobytes()
+
+ array_length = len(iterable)
+ fullfmt = "%s%d%s" % (endianess, array_length, datatype)
if datatype in ("s", "p"):
block = struct.pack(fullfmt, iterable)
@@ -808,7 +792,14 @@ def to_hp_block(
return to_binary_block(iterable, header, datatype, is_big_endian)
-def get_system_details(backends: bool = True) -> Dict[str, str]:
+# The actual value would be:
+# DebugInfo = Union[List[str], Dict[str, Union[str, DebugInfo]]]
+DebugInfo = Union[List[str], Dict[str, Any]]
+
+
+def get_system_details(
+ backends: bool = True,
+) -> Dict[str, Union[str, Dict[str, DebugInfo]]]:
"""Return a dictionary with information about the system."""
buildno, builddate = platform.python_build()
if sys.maxunicode == 65535:
@@ -821,7 +812,8 @@ def get_system_details(backends: bool = True) -> Dict[str, str]:
from . import __version__
- d = {
+ backend_details: Dict[str, DebugInfo] = OrderedDict()
+ d: Dict[str, Union[str, dict]] = {
"platform": platform.platform(),
"processor": platform.processor(),
"executable": sys.executable,
@@ -833,7 +825,7 @@ def get_system_details(backends: bool = True) -> Dict[str, str]:
"unicode": unitype,
"bits": bits,
"pyvisa": __version__,
- "backends": OrderedDict(),
+ "backends": backend_details,
}
if backends:
@@ -846,16 +838,16 @@ def get_system_details(backends: bool = True) -> Dict[str, str]:
try:
cls = highlevel.get_wrapper_class(backend)
except Exception as e:
- d["backends"][backend] = [
+ backend_details[backend] = [
"Could not instantiate backend",
"-> %s" % str(e),
]
continue
try:
- d["backends"][backend] = cls.get_debug_info()
+ backend_details[backend] = cls.get_debug_info()
except Exception as e:
- d["backends"][backend] = [
+ backend_details[backend] = [
"Could not obtain debug info",
"-> %s" % str(e),
]
@@ -946,17 +938,6 @@ def get_debug_info(to_screen=True):
print(out)
-def pip_install(package): # pragma: no cover
- warnings.warn("warn_for_invalid_kwargs will be removed in 1.12", FutureWarning)
- try:
- import pip # type: ignore
-
- return pip.main(["install", package])
- except ImportError:
- print(system_details_to_str(get_system_details()))
- raise RuntimeError("Please install pip to continue.")
-
-
machine_types = {
0: "UNKNOWN",
0x014C: "I386",
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 99d8a32..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,107 +0,0 @@
-[metadata]
-name = PyVISA
-author = Torsten Bronger, Gregor Thalhammer
-author_email = bronger@physik.rwth-aachen.de
-maintainer = Matthieu C. Dartiailh
-maintainer_email = m.dartiailh@gmail.com
-license = MIT License
-description = Python VISA bindings for GPIB, RS232, TCPIP and USB instruments
-keywords =
- VISA
- GPIB
- USB
- serial
- RS232
- measurement
- acquisition
-url = https://github.com/pyvisa/pyvisa
-long_description = file: README.rst, AUTHORS, CHANGES
-long_description_content_type = text/x-rst
-classifiers =
- Development Status :: 5 - Production/Stable
- Intended Audience :: Developers
- Intended Audience :: Science/Research
- License :: OSI Approved :: MIT License
- Operating System :: Microsoft :: Windows
- Operating System :: POSIX :: Linux
- Operating System :: MacOS :: MacOS X
- Programming Language :: Python
- Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
- Topic :: Software Development :: Libraries :: Python Modules
- Programming Language :: Python :: 3
- Programming Language :: Python :: 3.6
- Programming Language :: Python :: 3.7
- Programming Language :: Python :: 3.8
- Programming Language :: Python :: 3.9
-platforms = Linux,, Windows,, Mac
-
-[options]
-py_modules = visa
-packages =
- pyvisa
- pyvisa.ctwrapper
- pyvisa.resources
- pyvisa.thirdparty
- pyvisa.testsuite
- pyvisa.testsuite.fake-extensions
- pyvisa.testsuite.keysight_assisted_tests
-zip_safe = False
-setup_requires = setuptools_scm>=3.4.3
-install_requires =
- typing_extensions
- importlib-metadata; python_version<"3.8"
- dataclasses; python_version<"3.7"
-python_requires = >=3.6
-use_2to3 = False
-platforms = Linux; Windows; Mac
-test_suite = pyvisa.testsuite.testsuite
-setup_require = setuptools>=42; wheel; setuptools_scm[toml]>=3.4.3
-
-[options.entry_points]
-console_scripts =
- pyvisa-shell=pyvisa.cmd_line_tools:visa_shell
- pyvisa-info=pyvisa.cmd_line_tools:visa_info
-
-[options.package_data]
-* = *.dll
-pyvisa = py.typed
-
-[flake8]
-exclude =
- .git,
- __pycache__,
- docs/source/conf.py,
- old,
- build,
- dist,
- pyvisa/thirdparty/*,
- visa.py,
-ignore = E203, E266, E501, W503, E731
-# line length is intentionally set to 80 here because pyvisa uses Bugbear
-# See https://github.com/psf/black/blob/master/README.md#line-length for more details
-max-line-length = 80
-max-complexity = 18
-select = B,C,E,F,W,T4,B9
-per-file-ignores =
- pyvisa/__init__.py:E402
- pyvisa/constants.py:E221
-
-[mypy]
-follow_imports = ignore
-strict_optional = True
-
-# XXX Ideally remove once pytest ships typing information
-[mypy-pytest.*]
-ignore_missing_imports = True
-
-[mypy-pyvisa.thirdparty.*]
-ignore_errors = True
-
-[isort]
-multi_line_output = 3
-include_trailing_comma = true
-force_grid_wrap = 0
-use_parentheses = true
-line_length = 88
-skip = pyvisa/thirdparty/prettytable.py, pyvisa/__init__.py
-known_third_party = numpy,importlib_metadata,setuptools,typing_extensions, pytest
diff --git a/visa.py b/visa.py
deleted file mode 100644
index 87a8a69..0000000
--- a/visa.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Module to provide an import shortcut for the most common VISA operations.
-
-This file is part of PyVISA.
-
-:copyright: 2014-2019 by PyVISA Authors, see AUTHORS for more details.
-:license: MIT, see COPYING for more details.
-
-"""
-
-import warnings
-
-warnings.warn(
- (
- "The visa module provided by PyVISA is being deprecated. "
- "You can replace `import visa` by `import pyvisa as visa` "
- "to achieve the same effect.\n\n"
- "The reason for the deprecation is the possible conflict with "
- "the visa package provided by the "
- "https://github.com/visa-sdk/visa-python which can result in "
- "hard to debug situations."
- ),
- FutureWarning,
-)
-
-from pyvisa import logger, __version__, log_to_screen, constants
-from pyvisa.highlevel import ResourceManager
-from pyvisa.errors import (
- Error,
- VisaIOError,
- VisaIOWarning,
- VisaTypeError,
- UnknownHandler,
- OSNotSupported,
- InvalidBinaryFormat,
- InvalidSession,
- LibraryError,
-)
-
-# This is needed to registry all resources.
-from pyvisa.resources import Resource
-from pyvisa.cmd_line_tools import visa_main
-
-__all__ = [
- "ResourceManager",
- "constants",
- "logger",
- "Error",
- "VisaIOError",
- "VisaIOWarning",
- "VisaTypeError",
- "UnknownHandler",
- "OSNotSupported",
- "InvalidBinaryFormat",
- "InvalidSession",
- "LibraryError",
- "log_to_screen",
-]
-
-if __name__ == "__main__":
- visa_main()
More details
Historical runs
- patch-application-failed: Patch application failed: 0001-Skip-unreliable-tests.patch
- patch-application-failed: Patch application failed: 0001-Skip-unreliable-tests.patch
- push-failed: Failed to push result branch: Connection closed: Connection closed early The remote server unexpectedly closed the connection.
- push-failed: Failed to push result branch: Connection closed: Connection closed early The remote server unexpectedly closed the connection.