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

Full run details

Historical runs