New Upstream Release - python-pysnmp4-apps

Ready changes

Summary

Merged new upstream version: 0.4.1 (was: 0.3.2).

Resulting package

Built on 2022-03-16T12:54 (took 3m7s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases python3-pysnmp4-apps

Lintian Result

Diff

diff --git a/CHANGES b/CHANGES.txt
similarity index 69%
rename from CHANGES
rename to CHANGES.txt
index e4b962f..5d4c3ef 100644
--- a/CHANGES
+++ b/CHANGES.txt
@@ -1,11 +1,49 @@
-Revision 0.3.2
---------------
+Revision 0.4.1, released 12-02-2016
+-----------------------------------
+
+- Copyright notice added to non-trivial source code files.
+- Fix to stray trailing OID lurking to snmp*walk.py output.
+- Fix to __doc__ use in setup.py to make -O0 installation mode working.
+
+Revision 0.4.0, released 28-09-2015
+-----------------------------------
+
+- All apps renamed into snmp*.py and moved to scripts/ directory
+  for a more conventional layout.
+- All apps updated to support the latest pysnmp Standard Applications API.
+- Initial integration with the PySMI MIB compiler. The -P<x> option now
+  partially supported. All tools try to download missing ASN.1 MIBs from 
+  snmplabs.com web site by default.
+- Conditional variable buildValueOnly replaced with two buildObjectName
+  and buildValue at MibViewProxy.
+
+Revision 0.3.4, released 05-10-2013
+-----------------------------------
+
+- License updated to vanilla BSD 2-Clause to ease package use
+  (http://opensource.org/licenses/BSD-2-Clause).
+- Upper level exception handling improved for all apps.
+- Distribute is gone, switched to setuptools completely.
+- Fix to config.addTargetAddr() invocation -- since pysnmp 4.2.3, reworked
+  pysnmp LCD model does not require tagging transport address to bind it 
+  with communityName. 
+
+Revision 0.3.3, released 30-01-2013
+-----------------------------------
+
+- Tools will report pysnmp-apps package version along with pysnmp version
+  being used.
+- Package meta-information updated.
+- Package version is now in __init__.__version__
+
+Revision 0.3.2, released 21-04-2012
+-----------------------------------
 
 - MIB path mangiling reworked
 - Fix to make pysnmptrap INFORM C/L switch operational
 
-Revision 0.3.1
---------------
+Revision 0.3.1, released 06-11-2011
+-----------------------------------
 
 - Major overhawl for Python 2.4 -- 3.2 compatibility:
   + get rid of old-style types
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 2b1c283..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright (C) 1999-2012, Ilya Etingof <ilya@glas.net>, all rights reserved.
-
-THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY SITUATION
-ENDANGERING HUMAN LIFE OR PROPERTY.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-  * Redistributions of source code must retain the above copyright notice, this
-    list of conditions and the following disclaimer.
-
-  * Redistributions in binary form must reproduce the above copyright notice,
-    this list of conditions and the following disclaimer in the documentation
-    and/or other materials provided with the distribution.
-
-  * The name of the authors may not be used to endorse or promote products
-    derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..7ce5e4d
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,24 @@
+Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, 
+    this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. 
diff --git a/MANIFEST.in b/MANIFEST.in
index ce06902..604dd3c 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,2 @@
-include CHANGES LICENSE README
-recursive-include tools pysnmpget pysnmptranslate pysnmpwalk pysnmpbulkwalk pysnmpset pysnmptrap
+include *.txt
+recursive-include scripts *.py
diff --git a/PKG-INFO b/PKG-INFO
index cc920ae..438d352 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,21 +1,27 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: pysnmp-apps
-Version: 0.3.2
-Summary: PySNMP applications
+Version: 0.4.1
+Summary: SNMP command-line tools
 Home-page: http://sourceforge.net/projects/pysnmp/
-Author: Ilya Etingof
+Author: Ilya Etingof <ilya@glas.net>
 Author-email: ilya@glas.net
 License: BSD
-Description: UNKNOWN
-Platform: UNKNOWN
+Description: A collection of command-line tools for SNMP management purposes built on top of PySNMP package.
+Platform: any
 Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: System Administrators
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Education
 Classifier: Intended Audience :: Information Technology
+Classifier: Intended Audience :: System Administrators
 Classifier: Intended Audience :: Telecommunications Industry
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Natural Language :: English
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Communications
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: System :: Monitoring
 Classifier: Topic :: System :: Networking :: Monitoring
-Classifier: License :: OSI Approved :: BSD License
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/README b/README.txt
similarity index 79%
rename from README
rename to README.txt
index 421d74c..8366eee 100644
--- a/README
+++ b/README.txt
@@ -28,7 +28,7 @@ $ python setup.py install
 
 to install the whole thing.
 
-PySNMP version 4.1.x or later is required to run these tools.
+PySNMP version 4.3.x or later is required to run these tools.
 
 OPERATION
 ---------
@@ -36,7 +36,7 @@ OPERATION
 The most of PySNMP command-line tools could be run in a similar way as 
 their Net-SNMP counterparts. For example:
 
-$ pysnmpbulkwalk -v3 -u myuser -l authPriv -A myauthkey -X myprivkey localhost system
+$ snmpbulkwalk.py -v3 -u myuser -l authPriv -A myauthkey -X myprivkey localhost system
 SNMPv2-MIB::sysDescr.0 = DisplayString: Linux grommit 2.6.16.1 #2 PREEMPT Tue Apr 4 17:04:24 MSD 2006 i686 unknown unknown GNU/Linux
 SNMPv2-MIB::sysObjectID.0 = ObjectIdentifier: iso.org.dod.internet.private.enterprises.8072.3.2.101.3.6.1.4.1.8072.3.2.10
 SNMPv2-MIB::sysUpTime.0 = TimeTicks: 43 days 1:55:47.85372214785
@@ -44,12 +44,15 @@ SNMPv2-MIB::sysUpTime.0 = TimeTicks: 43 days 1:55:47.85372214785
 SNMPv2-MIB::sysORUpTime."8" = TimeStamp: 0 days 0:0:0.77
 SNMPv2-MIB::sysORUpTime."9" = TimeStamp: 0 days 0:0:0.77
 
-$ pysnmpget -v3 -u myuser -l authPriv -A myauthkey -X myprivkey localhost IP-MIB::ipAdEntBcastAddr.\"127.0.0.1\"
+$ snmpget.py -v3 -u myuser -l authPriv -A myauthkey -X myprivkey localhost IP-MIB::ipAdEntBcastAddr.\"127.0.0.1\"
 IP-MIB::ipAdEntBcastAddr."127.0.0.1" = Integer32: 1
 
-$ pysnmpset -v2c -c public localhost SNMPv2-MIB::sysDescr.0 = my-new-descr
+$ snmpset.py -v2c -c public localhost SNMPv2-MIB::sysDescr.0 = my-new-descr
 notWritable(17)
 
+To enjoy MIB resolution features make sure to have PySMI MIB Compiler 
+(http://sf.net/projects/pysmi) installed on your system.
+
 For more information, please, run any of these tools with --help option.
 
 GETTING HELP
diff --git a/debian/changelog b/debian/changelog
index 7eeb6c7..b27a2f0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-python-pysnmp4-apps (0.3.2-3) UNRELEASED; urgency=medium
+python-pysnmp4-apps (0.4.1-1) UNRELEASED; urgency=medium
 
   [ Ondřej Nový ]
   * d/control: Update Vcs-* fields with new Debian Python Team Salsa
@@ -10,8 +10,9 @@ python-pysnmp4-apps (0.3.2-3) UNRELEASED; urgency=medium
   [ Debian Janitor ]
   * Update watch file format version to 4.
   * Bump debhelper from old 12 to 13.
+  * New upstream release.
 
- -- Sandro Tosi <morph@debian.org>  Mon, 04 Jan 2021 17:06:58 -0500
+ -- Sandro Tosi <morph@debian.org>  Wed, 16 Mar 2022 12:51:48 -0000
 
 python-pysnmp4-apps (0.3.2-2.1) unstable; urgency=medium
 
diff --git a/pysnmp_apps.egg-info/PKG-INFO b/pysnmp_apps.egg-info/PKG-INFO
index cc920ae..438d352 100644
--- a/pysnmp_apps.egg-info/PKG-INFO
+++ b/pysnmp_apps.egg-info/PKG-INFO
@@ -1,21 +1,27 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: pysnmp-apps
-Version: 0.3.2
-Summary: PySNMP applications
+Version: 0.4.1
+Summary: SNMP command-line tools
 Home-page: http://sourceforge.net/projects/pysnmp/
-Author: Ilya Etingof
+Author: Ilya Etingof <ilya@glas.net>
 Author-email: ilya@glas.net
 License: BSD
-Description: UNKNOWN
-Platform: UNKNOWN
+Description: A collection of command-line tools for SNMP management purposes built on top of PySNMP package.
+Platform: any
 Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: System Administrators
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Education
 Classifier: Intended Audience :: Information Technology
+Classifier: Intended Audience :: System Administrators
 Classifier: Intended Audience :: Telecommunications Industry
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Natural Language :: English
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Communications
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: System :: Monitoring
 Classifier: Topic :: System :: Networking :: Monitoring
-Classifier: License :: OSI Approved :: BSD License
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/pysnmp_apps.egg-info/SOURCES.txt b/pysnmp_apps.egg-info/SOURCES.txt
index 8c8fa08..d151fa4 100644
--- a/pysnmp_apps.egg-info/SOURCES.txt
+++ b/pysnmp_apps.egg-info/SOURCES.txt
@@ -1,7 +1,9 @@
-CHANGES
-LICENSE
+CHANGES.txt
+LICENSE.txt
 MANIFEST.in
-README
+README.txt
+requirements.txt
+setup.cfg
 setup.py
 pysnmp_apps/__init__.py
 pysnmp_apps/error.py
@@ -20,9 +22,9 @@ pysnmp_apps/cli/pdu.py
 pysnmp_apps/cli/secmod.py
 pysnmp_apps/cli/spark.py
 pysnmp_apps/cli/target.py
-tools/pysnmpbulkwalk
-tools/pysnmpget
-tools/pysnmpset
-tools/pysnmptranslate
-tools/pysnmptrap
-tools/pysnmpwalk
\ No newline at end of file
+scripts/snmpbulkwalk.py
+scripts/snmpget.py
+scripts/snmpset.py
+scripts/snmptranslate.py
+scripts/snmptrap.py
+scripts/snmpwalk.py
\ No newline at end of file
diff --git a/pysnmp_apps.egg-info/requires.txt b/pysnmp_apps.egg-info/requires.txt
index fa6b16f..4d0499f 100644
--- a/pysnmp_apps.egg-info/requires.txt
+++ b/pysnmp_apps.egg-info/requires.txt
@@ -1 +1 @@
-pysnmp>=4.2.2
\ No newline at end of file
+pysnmp>=4.3.0
diff --git a/pysnmp_apps/__init__.py b/pysnmp_apps/__init__.py
index 0fff5dd..ae0ff01 100644
--- a/pysnmp_apps/__init__.py
+++ b/pysnmp_apps/__init__.py
@@ -1 +1,2 @@
-"""Various components of SNMP applications"""
+# http://www.python.org/dev/peps/pep-0396/
+__version__ = '0.4.1'
diff --git a/pysnmp_apps/cli/__init__.py b/pysnmp_apps/cli/__init__.py
index 1d1fa20..8c3066b 100644
--- a/pysnmp_apps/cli/__init__.py
+++ b/pysnmp_apps/cli/__init__.py
@@ -1 +1 @@
-# Command-line arguments parsers for pysnmp-apps
+# This file is necessary to make this directory a package.
diff --git a/pysnmp_apps/cli/base.py b/pysnmp_apps/cli/base.py
index 50e6ee2..eceb7f2 100644
--- a/pysnmp_apps/cli/base.py
+++ b/pysnmp_apps/cli/base.py
@@ -1,4 +1,9 @@
-# Abstract interface to SNMP objects initializers
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
 import sys
 from pysnmp_apps.cli import spark
 
diff --git a/pysnmp_apps/cli/main.py b/pysnmp_apps/cli/main.py
index 4a714b0..1095e66 100644
--- a/pysnmp_apps/cli/main.py
+++ b/pysnmp_apps/cli/main.py
@@ -1,6 +1,30 @@
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
+import sys
 from pysnmp.smi import view
 from pysnmp_apps.cli import base
-from pysnmp import error, majorVersionId
+from pysnmp import error
+from pysnmp_apps import __version__ as pysnmpAppsVersion
+try:
+    from pysnmp import __version__ as pysnmpVersion
+except ImportError:
+    pysnmpVersion = 'N/A'
+try:
+    from pysmi import __version__ as pysmiVersion
+except ImportError:
+    pysmiVersion = 'N/A'
+try:
+    from pysnmp_mibs import __version__ as pysnmpMibsVersion
+except ImportError:
+    pysnmpMibsVersion = 'N/A'
+try:
+    from pyasn1 import __version__ as pyasn1Version
+except ImportError:
+    pyasn1Version = 'N/A'
 try:
     from pysnmp import debug
 except ImportError:
@@ -10,22 +34,31 @@ except ImportError:
 
 def getUsage():
     return "\
-PySNMP library version %s; http://pysnmp.sf.net\n\
+Command-line SNMP tools version %s, written by Ilya Etingof <ilya@glas.net>\n\
+Foundation libraries: pysmi %s, pysnmp %s, pysnmp-mibs %s, pyasn1 %s\n\
+Python interpreter: %s\n\
+Software documentation and support at http://pysnmp.sf.net\n\
    -h                    display this help message\n\
    -V                    software release information\n\
    -d                    dump raw packets\n\
    -D category           enable debugging [%s]\n\
-" % (majorVersionId, debug and ','.join(debug.flagMap.keys()) or "")
+" % ( pysnmpAppsVersion,
+      pysmiVersion,
+      pysnmpVersion,
+      pysnmpMibsVersion,
+      pyasn1Version,
+      sys.version.replace('\n', ''),
+      debug and ','.join(debug.flagMap.keys()) or "")
     
 # Scanner
 
 class MainScannerMixIn:
     def t_help(self, s):
-        r' -h '
+        r' -h|--help '
         self.rv.append(base.ConfigToken('help'))
 
     def t_versioninfo(self, s):
-        r' -V '
+        r' -V|--version '
         self.rv.append(base.ConfigToken('versioninfo'))
 
     def t_dump(self, s):
@@ -33,7 +66,7 @@ class MainScannerMixIn:
         self.rv.append(base.ConfigToken('dump'))
 
     def t_debug(self, s):
-        r' -D '
+        r' -D|--debug '
         self.rv.append(base.ConfigToken('debug'))
 
 # Parser
@@ -97,6 +130,6 @@ class __MainGenerator(base.GeneratorTemplate):
 def generator(cbCtx, ast):
     snmpEngine, ctx = cbCtx
     ctx['mibViewController'] = view.MibViewController(
-        snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
-        )
+        snmpEngine.getMibBuilder()
+    )
     return __MainGenerator().preorder((snmpEngine, ctx), ast)
diff --git a/pysnmp_apps/cli/mibview.py b/pysnmp_apps/cli/mibview.py
index cf64765..54a8137 100644
--- a/pysnmp_apps/cli/mibview.py
+++ b/pysnmp_apps/cli/mibview.py
@@ -1,19 +1,38 @@
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
 # C/L interface to MIB variables. Mimics Net-SNMP CLI.
+#
 import os
 from pyasn1.type import univ
 from pysnmp_apps.cli import base
 from pysnmp.proto import rfc1902
-from pysnmp.smi import builder
+from pysnmp.smi import builder, compiler
 from pysnmp import error
 
+defaultMibSourceUrl = 'http://mibs.snmplabs.com/asn1/@mib@'
+defaultMibBorrowerUrl = 'http://mibs.snmplabs.com/pysnmp/fulltexts/@mib@'
+
 # Usage
 
 def getUsage():
     return "\
 MIB options:\n\
-   -m MIB[:...]      load given list of MIBs (ALL loads everything)\n\
-   -M DIR[:...]      look in given list of directories for MIBs\n\
-   -O OUTOPTS        Toggle various defaults controlling output display:\n\
+   -m MIB[:...]   load given list of MIBs (ALL loads all compiled MIBs)\n\
+   -M DIR[:...]   look in given list of directories for MIBs\n\
+   -P MIBOPTS     Toggle various defaults controlling MIB parsing:\n\
+              XS: search for ASN.1 MIBs in remote directories specified\n\
+                  in URL form. The @mib@ token in the URL is substituted\n\
+                  with actual MIB to be downloaded. Default repository\n\
+                  address is %s\n\
+              XB: search for pysnmp MIBs in remote directories specified\n\
+                  in URL form. The @mib@ token in the URL is substituted\n\
+                  with actual MIB to be downloaded. Default repository\n\
+                  address is %s\n\
+   -O OUTOPTS     Toggle various defaults controlling output display:\n\
               q:  removes the equal sign and type information\n\
               Q:  removes the type information\n\
               f:  print full OIDs on output\n\
@@ -29,10 +48,10 @@ MIB options:\n\
               v:  print values only (not OID = value)\n\
               U:  don't print units\n\
               t:  output timeticks values as raw numbers\n\
-   -I INOPTS         Toggle various defaults controlling input parsing:\n\
+   -I INOPTS      Toggle various defaults controlling input parsing:\n\
               h:  don't apply DISPLAY-HINTs\n\
               u:  top-level OIDs must have '.' prefix (UCD-style)\n\
-"
+" % (defaultMibSourceUrl, defaultMibBorrowerUrl)
 
 # Scanner
 
@@ -45,6 +64,10 @@ class MibViewScannerMixIn:
         r' -M '
         self.rv.append(base.ConfigToken('mibdirs'))
 
+    def t_parseropts(self, s):
+        r' -P '
+        self.rv.append(base.ConfigToken('parseropts'))
+
     def t_outputopts(self, s):
         r' -O '
         self.rv.append(base.ConfigToken('outputopts'))
@@ -59,6 +82,7 @@ class MibViewParserMixIn:
     def p_mibView(self, args):
         '''
         Option ::= GeneralOption
+        Option ::= ParserOption
         Option ::= OutputOption
         Option ::= InputOption
 
@@ -75,6 +99,12 @@ class MibViewParserMixIn:
         MibFiles ::= MibFile
         MibFile ::= string
 
+        ParserOption ::= parseropts string
+        ParserOption ::= parseropts whitespace string
+        ParserOption ::= parseropts string whitespace Url
+        ParserOption ::= parseropts whitespace string whitespace Url
+        Url ::= string semicolon string
+
         OutputOption ::= outputopts string
         OutputOption ::= outputopts whitespace string
 
@@ -88,7 +118,7 @@ class __MibViewGenerator(base.GeneratorTemplate):
     # Load MIB modules
     def n_MibFile(self, cbCtx, node):
         snmpEngine, ctx = cbCtx
-        mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
+        mibBuilder = snmpEngine.getMibBuilder()
         if node[0].attr.lower() == 'all':
             mibBuilder.loadModules()
         else:
@@ -96,18 +126,37 @@ class __MibViewGenerator(base.GeneratorTemplate):
             
     def n_MibDir(self, cbCtx, node):
         snmpEngine, ctx = cbCtx
-        mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
-        mibBuilder.setMibSources(
-            *mibBuilder.getMibSources() + (builder.ZipMibSource(node[0].attr).init(),)
-        )
+        if 'MibDir' not in ctx:
+            ctx['MibDir'] = []
+        ctx['MibDir'].append(node[0].attr)
+
+    def n_Url(self, cbCtx, node):
+        snmpEngine, ctx = cbCtx
+        ctx['Url'] = node[0].attr+':'+node[2].attr
+
+    def n_ParserOption_exit(self, cbCtx, node):
+        snmpEngine, ctx = cbCtx
+        opt = node[1].attr or node[2].attr
+        for c in opt:
+            if c == 'XS':
+                if 'MibDir' not in ctx:
+                    ctx['MibDir'] = []
+                if 'Url' not in ctx:
+                    raise error.PySnmpError('Missing URL for option')
+                ctx['MibDir'].append(ctx['Url'])
+                del ctx['Url']
+            elif c == 'XB':
+                if 'MibBorrowers' not in ctx:
+                    ctx['MibBorrowers'] = []
+                if 'Url' not in ctx:
+                    raise error.PySnmpError('Missing URL for option')
+                ctx['MibBorrowers'].append(ctx['Url'])
+                del ctx['Url']
 
     def n_OutputOption(self, cbCtx, node):
         snmpEngine, ctx = cbCtx
         mibViewProxy = ctx['mibViewProxy']
-        if len(node) > 2:
-            opt = node[2].attr
-        else:
-            opt = node[1].attr
+        opt = node[1].attr or node[2].attr
         for c in opt:
             if c == 'q':
                 mibViewProxy.buildEqualSign = 0
@@ -142,7 +191,7 @@ class __MibViewGenerator(base.GeneratorTemplate):
             elif c == 'T':
                 mibViewProxy.buildHexVals = 1
             elif c == 'v':
-                mibViewProxy.buildValueOnly = 1
+                mibViewProxy.buildObjectName = 0
             elif c == 'U':
                 mibViewProxy.buildUnits = 0
             elif c == 't':
@@ -158,10 +207,7 @@ class __MibViewGenerator(base.GeneratorTemplate):
     def n_InputOption(self, cbCtx, node):
         snmpEngine, ctx = cbCtx
         mibViewProxy = ctx['mibViewProxy']
-        if len(node) > 2:
-            opt = node[2].attr
-        else:
-            opt = node[1].attr
+        opt = node[1].attr or node[2].attr
         for c in opt:
             if c == 'R':
                 pass
@@ -184,7 +230,20 @@ def generator(cbCtx, ast):
     snmpEngine, ctx = cbCtx
     if 'mibViewProxy' not in ctx:
         ctx['mibViewProxy'] = MibViewProxy(ctx['mibViewController'])
-    return __MibViewGenerator().preorder((snmpEngine, ctx), ast)
+
+    compiler.addMibCompiler(snmpEngine.getMibBuilder())
+
+    snmpEngine, ctx = __MibViewGenerator().preorder((snmpEngine, ctx), ast)
+
+    if 'MibDir' not in ctx:
+        ctx['MibDir'] = [ defaultMibSourceUrl ]
+    if 'MibBorrowers' not in ctx:
+        ctx['MibBorrowers'] = [ defaultMibBorrowerUrl ]
+
+    compiler.addMibCompiler(snmpEngine.getMibBuilder(),
+                            sources=ctx['MibDir'],
+                            borrowers=ctx['MibBorrowers'])
+    return snmpEngine, ctx
 
 class UnknownSyntax:
     def prettyOut(self, val):
@@ -205,6 +264,8 @@ class MibViewProxy:
     # currently N/A
     
     # MIB output options
+    buildObjectName = 1
+    buildValue = 1
     buildModInfo = 1
     buildObjectDesc = 1
     buildNumericName = 0
@@ -218,7 +279,6 @@ class MibViewProxy:
     buildRawVals = 0
     buildRawTimeTicks = 0
     buildGuessedStringVals = 1
-    buildValueOnly = 0
     buildUnits = 1
     
     # MIB input options
@@ -252,7 +312,7 @@ class MibViewProxy:
         modName, nodeDesc, _suffix = mibViewController.getNodeLocation(prefix)
         out = ''
         # object name
-        if not self.buildValueOnly:        
+        if self.buildObjectName:        
             if self.buildModInfo:
                 out = '%s::' % modName
             if self.buildObjectDesc:
@@ -289,58 +349,62 @@ class MibViewProxy:
                             out = out + '.' + '.'.join(
                                 [ str(x) for x in suffix ]
                                 )
+
+        if self.buildObjectName and self.buildValue:
             if self.buildEqualSign:
                 out = out + ' = '
             else:
                 out = out + ' '
 
         # Value
-        if isinstance(val, univ.Null):
-            return out + val.prettyPrint()
-        mibNode, = mibViewController.mibBuilder.importSymbols(
-            modName, nodeDesc
+        if self.buildValue:
+            if isinstance(val, univ.Null):
+                return out + val.prettyPrint()
+            mibNode, = mibViewController.mibBuilder.importSymbols(
+                modName, nodeDesc
             )
-        if hasattr(mibNode, 'syntax'):
-            syntax = mibNode.syntax
-        else:
-            syntax = val
-        if syntax is None: # lame Agent may return a non-instance OID
-            syntax = unknownSyntax
-        if self.buildTypeInfo:
-            out = out + '%s: ' % syntax.__class__.__name__
-        if self.buildRawVals:
-            out = out + str(val)
-        elif self.buildHexVals: # XXX make it always in hex?
-            if self.__intValue.isSuperTypeOf(val):
-                out = out + '%x' % int(val)
-            elif self.__oidValue.isSuperTypeOf(val):
-                out = out + ' '.join([ '%x' % x for x in tuple(val) ])
+            if hasattr(mibNode, 'syntax'):
+                syntax = mibNode.syntax
             else:
-                out = out + ' '.join([ '%.2x' % ord(x) for x in str(val) ])
-        elif self.__timeValue.isSameTypeWith(val):
-            if self.buildRawTimeTicks:
-                out = out + str(int(val))
-            else: # TimeTicks is not a TC
-                val = int(val)
-                d, m = divmod(val, 8640000)
-                out = out + '%d days ' % d
-                d, m = divmod(m, 360000)
-                out = out + '%d:' % d
-                d, m = divmod(m, 6000)
-                out = out + '%d:' % d
-                d, m = divmod(m, 100)
-                out = out + '%d.%d' % (d, m)
-        elif self.__oidValue.isSuperTypeOf(val):
-            oid, label, suffix = mibViewController.getNodeName(val)
-            out = out + '.'.join(
-                label + tuple([ str(x) for x in suffix ])
+                syntax = val
+            if syntax is None: # lame Agent may return a non-instance OID
+                syntax = unknownSyntax
+            if self.buildTypeInfo:
+                out = out + '%s: ' % syntax.__class__.__name__
+            if self.buildRawVals:
+                out = out + str(val)
+            elif self.buildHexVals: # XXX make it always in hex?
+                if self.__intValue.isSuperTypeOf(val):
+                    out = out + '%x' % int(val)
+                elif self.__oidValue.isSuperTypeOf(val):
+                    out = out + ' '.join([ '%x' % x for x in tuple(val) ])
+                else:
+                    out = out + ' '.join([ '%.2x' % ord(x) for x in str(val) ])
+            elif self.__timeValue.isSameTypeWith(val):
+                if self.buildRawTimeTicks:
+                    out = out + str(int(val))
+                else: # TimeTicks is not a TC
+                    val = int(val)
+                    d, m = divmod(val, 8640000)
+                    out = out + '%d days ' % d
+                    d, m = divmod(m, 360000)
+                    out = out + '%d:' % d
+                    d, m = divmod(m, 6000)
+                    out = out + '%d:' % d
+                    d, m = divmod(m, 100)
+                    out = out + '%d.%d' % (d, m)
+            elif self.__oidValue.isSuperTypeOf(val):
+                oid, label, suffix = mibViewController.getNodeName(val)
+                out = out + '.'.join(
+                    label + tuple([ str(x) for x in suffix ])
                 )
-        else:
-            out = out + syntax.prettyOut(val)
+            else:
+                out = out + syntax.prettyOut(val)
+
+            if self.buildUnits:
+                if hasattr(mibNode, 'getUnits'):
+                    out = out + ' %s' % mibNode.getUnits()
 
-        if self.buildUnits:
-            if hasattr(mibNode, 'getUnits'):
-                out = out + ' %s' % mibNode.getUnits()
         return out
     
     def setPrettyOidValue(self, oid, val, t):
diff --git a/pysnmp_apps/cli/msgmod.py b/pysnmp_apps/cli/msgmod.py
index 3173451..75e8dee 100644
--- a/pysnmp_apps/cli/msgmod.py
+++ b/pysnmp_apps/cli/msgmod.py
@@ -1,3 +1,9 @@
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
 from pysnmp_apps.cli import base
 from pysnmp import error
 
diff --git a/pysnmp_apps/cli/pdu.py b/pysnmp_apps/cli/pdu.py
index c29e26f..693a8f5 100644
--- a/pysnmp_apps/cli/pdu.py
+++ b/pysnmp_apps/cli/pdu.py
@@ -1,3 +1,9 @@
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
 import sys
 from pyasn1.type import univ
 from pyasn1.error import PyAsn1Error
diff --git a/pysnmp_apps/cli/secmod.py b/pysnmp_apps/cli/secmod.py
index a32e9f2..c94174f 100644
--- a/pysnmp_apps/cli/secmod.py
+++ b/pysnmp_apps/cli/secmod.py
@@ -1,3 +1,9 @@
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
 from pysnmp_apps.cli import base
 from pysnmp.entity import config
 from pysnmp import error
@@ -18,7 +24,7 @@ SNMPv3 security options:\n\
    -E CONTEXT-ENGINE-ID  authoritative context engine ID\n\
    -e ENGINE-ID          authoritative SNMP engine ID (will discover)\n\
    -n CONTEXT-NAME       authoritative context name\n\
-   -Z ENGINE-BOOTS       local SNMP engine uptime\n\
+   -Z BOOTS,TIME         destination SNMP engine boots/uptime\n\
 "
 
 # Scanner
@@ -209,6 +215,10 @@ class __SMGenerator(base.GeneratorTemplate):
             ctx['engineBoots'] = node[2].attr
         else:
             ctx['engineBoots'] = node[1].attr
+        if ',' in ctx['engineBoots']:
+            ctx['engineBoots'], ctx['engineTime'] = ctx['engineBoots'].split(',', 1)
+        else:
+            ctx['engineTime'] = 0
 
 def generator(cbCtx, ast):
     snmpEngine, ctx = cbCtx
@@ -244,7 +254,17 @@ def generator(cbCtx, ast):
             ctx['privProtocol'],
             ctx['privKey']
             )
-
+        # edit SNMP engine boots/uptime
+        if 'engineBoots' in ctx:
+            snmpEngineBoots, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineBoots')
+            snmpEngineBoots.setSyntax(
+                snmpEngineBoots.getSyntax().clone(ctx['engineBoots'])
+            )
+        if 'engineTime' in ctx:
+            snmpEngineTime, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineTime')
+            snmpEngineTime.setSyntax(
+                snmpEngineTime.getSyntax().clone(ctx['engineTime'])
+            )
     else: # SNMPv1/v2c
         if 'communityName' not in ctx:
             raise error.PySnmpError('Community name not specified')            
@@ -254,7 +274,7 @@ def generator(cbCtx, ast):
             snmpEngine,
             ctx['securityName'],
             ctx['communityName']
-            )
+        )
 
     ctx['paramsName'] = '%s-params' % ctx['securityName']
     config.addTargetParams(
diff --git a/pysnmp_apps/cli/spark.py b/pysnmp_apps/cli/spark.py
index e042aad..d5979c5 100644
--- a/pysnmp_apps/cli/spark.py
+++ b/pysnmp_apps/cli/spark.py
@@ -1,24 +1,30 @@
-#  Copyright (c) 1998-2000 John Aycock
-#  
-#  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 the Software without restriction, including
-#  without limitation the rights to use, copy, modify, merge, publish,
-#  distribute, sublicense, and/or sell copies of the Software, and to
-#  permit persons to whom the Software is furnished to do so, subject to
-#  the following conditions:
-#  
-#  The above copyright notice and this permission notice shall be
-#  included in all copies or substantial portions of the Software.
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
+# Copyright (c) 1998-2000 John Aycock
 #  
-#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-#  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-#  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-#  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
+# 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 the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
 __version__ = 'SPARK-0.6.1'
 
 import re
diff --git a/pysnmp_apps/cli/target.py b/pysnmp_apps/cli/target.py
index 11a1c04..6e40cc2 100644
--- a/pysnmp_apps/cli/target.py
+++ b/pysnmp_apps/cli/target.py
@@ -1,3 +1,9 @@
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
 import socket
 from pysnmp_apps.cli import base
 from pysnmp.carrier.asynsock.dgram import udp, udp6
@@ -160,7 +166,6 @@ class __TargetGeneratorPassTwo(base.GeneratorTemplate):
     def n_Agent_exit(self, cbCtx, node):
         snmpEngine, ctx = cbCtx
         ctx['addrName'] = '%s-name' % ctx['paramsName']
-        ctx['transportTag'] = '%s-tag' % ctx['addrName']
         config.addTargetAddr(
             snmpEngine,
             ctx['addrName'],
@@ -170,7 +175,7 @@ class __TargetGeneratorPassTwo(base.GeneratorTemplate):
             # net-snmp defaults
             ctx.get('timeout', 100),
             ctx.get('retryCount', 5),
-            tagList=ctx['transportTag']
+            tagList=ctx.get('transportTag', '')
             )
         config.addSocketTransport(
             snmpEngine,
diff --git a/pysnmp_apps/error.py b/pysnmp_apps/error.py
index 6cbf960..6b8018e 100644
--- a/pysnmp_apps/error.py
+++ b/pysnmp_apps/error.py
@@ -1,5 +1,9 @@
-"""Top-level exception classes
-"""   
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
 from pysnmp import error
 
 class SnmpApplicationError(error.PySnmpError): pass
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..4d0499f
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+pysnmp>=4.3.0
diff --git a/tools/pysnmpbulkwalk b/scripts/snmpbulkwalk.py
similarity index 67%
rename from tools/pysnmpbulkwalk
rename to scripts/snmpbulkwalk.py
index 1f48aab..ef7a03b 100755
--- a/tools/pysnmpbulkwalk
+++ b/scripts/snmpbulkwalk.py
@@ -1,10 +1,12 @@
 #!/usr/bin/env python
+# This file is part of pysnmp-apps software.
 #
-# GETBULK command generator
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
 #
-# Copyright 1999-2012 by Ilya Etingof <ilya@glas.net>.
+# GETBULK command generator
 #
-import sys, time
+import sys, time, traceback
 from pysnmp_apps.cli import main, msgmod, secmod, target, pdu, mibview, base
 from pysnmp.entity import engine
 from pysnmp.entity.rfc3413 import cmdgen
@@ -21,40 +23,34 @@ GETBULK options:\n\
               c:       do not check returned OIDs are increasing\n\
               t:       display wall-clock time to complete the request\n\
               p:       print the number of variables found\n\
-%s%s" % (
-        sys.argv[0],
-        main.getUsage(),
-        msgmod.getUsage(),
-        secmod.getUsage(),
-        mibview.getUsage(),
-        target.getUsage(),
-        pdu.getReadUsage()
-        )
+%s%s" % (sys.argv[0],
+         main.getUsage(),
+         msgmod.getUsage(),
+         secmod.getUsage(),
+         mibview.getUsage(),
+         target.getUsage(),
+         pdu.getReadUsage())
 
 # Construct c/l interpreter for this app
 
-class Scanner(
-    msgmod.MPScannerMixIn,
-    secmod.SMScannerMixIn,
-    mibview.MibViewScannerMixIn,
-    target.TargetScannerMixIn,
-    pdu.ReadPduScannerMixIn,
-    main.MainScannerMixIn,
-    base.ScannerTemplate
-    ):
+class Scanner(msgmod.MPScannerMixIn,
+              secmod.SMScannerMixIn,
+              mibview.MibViewScannerMixIn,
+              target.TargetScannerMixIn,
+              pdu.ReadPduScannerMixIn,
+              main.MainScannerMixIn,
+              base.ScannerTemplate):
     def t_appopts(self, s):
         r' -C '
         self.rv.append(base.ConfigToken('appopts'))
 
-class Parser(
-    msgmod.MPParserMixIn,
-    secmod.SMParserMixIn,
-    mibview.MibViewParserMixIn,
-    target.TargetParserMixIn,
-    pdu.ReadPduParserMixIn,
-    main.MainParserMixIn,
-    base.ParserTemplate
-    ):
+class Parser(msgmod.MPParserMixIn,
+             secmod.SMParserMixIn,
+             mibview.MibViewParserMixIn,
+             target.TargetParserMixIn,
+             pdu.ReadPduParserMixIn,
+             main.MainParserMixIn,
+             base.ParserTemplate):
     def p_appOptions(self, args):
         '''
         Option ::= ApplicationOption
@@ -98,35 +94,8 @@ def generator(cbCtx, ast):
     snmpEngine, ctx = cbCtx
     return __Generator().preorder((snmpEngine, ctx), ast)
     
-snmpEngine = engine.SnmpEngine()
-
-try:
-    # Parse c/l into AST
-    ast = Parser().parse(
-        Scanner().tokenize(' '.join(sys.argv[1:]))
-        )
-
-    ctx = {}
-
-    # Apply configuration to SNMP entity
-    main.generator((snmpEngine, ctx), ast)
-    msgmod.generator((snmpEngine, ctx), ast)
-    secmod.generator((snmpEngine, ctx), ast)    
-    mibview.generator((snmpEngine, ctx), ast)
-    target.generator((snmpEngine, ctx), ast)
-    pdu.readPduGenerator((snmpEngine, ctx), ast)
-    generator((snmpEngine, ctx), ast)
-
-except error.PySnmpError:
-    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
-    sys.exit(-1)
-
-ctx['myHeadVars'] = [ rfc1902.ObjectName(x[0]) for x in ctx['varBinds'] ]
-    
-# Run SNMP engine
-
-def cbFun(sendRequestHandle, errorIndication, errorStatus, errorIndex,
-          varBindTable, cbCtx):
+def cbFun(snmpEngine, sendRequestHandle, errorIndication,
+          errorStatus, errorIndex, varBindTable, cbCtx):
     if errorIndication:
         if errorIndication != 'oidNotIncreasing' or \
                not ctx.get('ignoreNonIncreasingOids'):
@@ -142,29 +111,64 @@ def cbFun(sendRequestHandle, errorIndication, errorStatus, errorIndex,
     for varBindRow in varBindTable:
         colIdx = -1; inTableFlag = 0
         for oid, val in varBindRow:
-            colIdx = colIdx + 1
-            sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
-                cbCtx['mibViewController'], oid, val
-                ))
+            colIdx += 1
             if cbCtx['myHeadVars'][colIdx].isPrefixOf(oid):
-                inTableFlag = 1
+                sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
+                    cbCtx['mibViewController'], oid, val
+                    )
+                )
+                inTableFlag += 1
         if cbCtx.get('reportFoundVars'):
-            cbCtx['reportFoundVars'] = cbCtx['reportFoundVars'] + len(varBindRow)
+            cbCtx['reportFoundVars'] += inTableFlag
         if not inTableFlag:
             return # stop on end-of-table
     return 1 # continue walking
 
-cmdgen.BulkCommandGenerator().sendReq(
-    snmpEngine, ctx['addrName'],
-    ctx.get('nonRepeaters', 0), ctx.get('maxRepetitions', 25),
-    ctx['varBinds'], cbFun, ctx,
-    ctx.get('contextEngineId'), ctx.get('contextName', '')
-    )
+# Run SNMP engine
 
+snmpEngine = engine.SnmpEngine()
+ 
 try:
+    # Parse c/l into AST
+    ast = Parser().parse(
+        Scanner().tokenize(' '.join(sys.argv[1:]))
+    )
+
+    ctx = {}
+
+    # Apply configuration to SNMP entity
+    main.generator((snmpEngine, ctx), ast)
+    msgmod.generator((snmpEngine, ctx), ast)
+    secmod.generator((snmpEngine, ctx), ast)    
+    mibview.generator((snmpEngine, ctx), ast)
+    target.generator((snmpEngine, ctx), ast)
+    pdu.readPduGenerator((snmpEngine, ctx), ast)
+    generator((snmpEngine, ctx), ast)
+
+    ctx['myHeadVars'] = [ rfc1902.ObjectName(x[0]) for x in ctx['varBinds'] ]
+
+    cmdgen.BulkCommandGenerator().sendVarBinds(
+        snmpEngine,
+        ctx['addrName'],
+        ctx.get('contextEngineId'), ctx.get('contextName', ''),
+        ctx.get('nonRepeaters', 0), ctx.get('maxRepetitions', 25),
+        ctx['varBinds'],
+        cbFun, ctx
+    )
+
     snmpEngine.transportDispatcher.runDispatcher()
+
+except KeyboardInterrupt:
+    sys.stderr.write('Shutting down...\n')
+
 except error.PySnmpError:
-    sys.stderr.write('Error: %s\n' % sys.exc_info()[1])
+    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
+    sys.exit(-1)
+
+except Exception:
+    sys.stderr.write('Process terminated: %s\n' % sys.exc_info()[1])
+    for line in traceback.format_exception(*sys.exc_info()):
+        sys.stderr.write(line.replace('\n', ';'))
     sys.exit(-1)
 
 if ctx.get('reportFoundVars'):
diff --git a/scripts/snmpget.py b/scripts/snmpget.py
new file mode 100755
index 0000000..60616b5
--- /dev/null
+++ b/scripts/snmpget.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
+# GET command generator
+#
+import sys,traceback
+from pysnmp_apps.cli import main, msgmod, secmod, target, pdu, mibview, base
+from pysnmp.entity import engine
+from pysnmp.entity.rfc3413 import cmdgen
+from pysnmp import error
+
+def getUsage():
+    return "Usage: %s [OPTIONS] <AGENT> <PARAMETERS>\n\
+%s%s%s%s%s%s" % (sys.argv[0],
+                 main.getUsage(),
+                 msgmod.getUsage(),
+                 secmod.getUsage(),
+                 mibview.getUsage(),
+                 target.getUsage(),
+                 pdu.getReadUsage())
+
+# Construct c/l interpreter for this app
+
+class Scanner(msgmod.MPScannerMixIn,
+              secmod.SMScannerMixIn,
+              mibview.MibViewScannerMixIn,
+              target.TargetScannerMixIn,
+              pdu.ReadPduScannerMixIn,
+              main.MainScannerMixIn,
+              base.ScannerTemplate): pass
+
+class Parser(msgmod.MPParserMixIn,
+             secmod.SMParserMixIn,
+             mibview.MibViewParserMixIn,
+             target.TargetParserMixIn,
+             pdu.ReadPduParserMixIn,
+             main.MainParserMixIn,
+             base.ParserTemplate): pass
+
+def cbFun(snmpEngine, sendRequestHandle, errorIndication,
+          errorStatus, errorIndex, varBinds, cbCtx):
+    if errorIndication:
+        sys.stderr.write('%s\n' % errorIndication)
+    elif errorStatus:
+        sys.stderr.write(
+            '%s at %s\n' %
+            ( errorStatus.prettyPrint(),
+              errorIndex and varBinds[int(errorIndex)-1] or '?' )
+            )
+    else:
+        for oid, val in varBinds:
+            sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
+                    cbCtx['mibViewController'], oid, val
+                )
+            )
+
+# Run SNMP engine
+
+snmpEngine = engine.SnmpEngine()
+
+try:
+    # Parse c/l into AST
+    ast = Parser().parse(
+        Scanner().tokenize(' '.join(sys.argv[1:]))
+    )
+
+    ctx = {}
+
+    # Apply configuration to SNMP entity
+    main.generator((snmpEngine, ctx), ast)
+    msgmod.generator((snmpEngine, ctx), ast)
+    secmod.generator((snmpEngine, ctx), ast)    
+    mibview.generator((snmpEngine, ctx), ast)
+    target.generator((snmpEngine, ctx), ast)
+    pdu.readPduGenerator((snmpEngine, ctx), ast)
+
+    cmdgen.GetCommandGenerator().sendVarBinds(
+        snmpEngine,
+        ctx['addrName'],
+        ctx.get('contextEngineId'), ctx.get('contextName', ''),
+        ctx['varBinds'],
+        cbFun, ctx
+    )
+
+    snmpEngine.transportDispatcher.runDispatcher()
+
+except KeyboardInterrupt:
+    sys.stderr.write('Shutting down...\n')
+
+except error.PySnmpError:
+    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
+    sys.exit(-1)
+
+except Exception:
+    sys.stderr.write('Process terminated\n')
+    for line in traceback.format_exception(*sys.exc_info()):
+        sys.stderr.write(line.replace('\n', ';'))
+    sys.exit(-1)
diff --git a/scripts/snmpset.py b/scripts/snmpset.py
new file mode 100755
index 0000000..da2781d
--- /dev/null
+++ b/scripts/snmpset.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
+# SET command generator
+#
+import sys, traceback
+from pysnmp_apps.cli import main, msgmod, secmod, target, pdu, mibview, base
+from pysnmp.entity import engine
+from pysnmp.entity.rfc3413 import cmdgen
+from pysnmp import error
+
+def getUsage():
+    return "Usage: %s [OPTIONS] <AGENT> <PARAMETERS>\n\
+%s%s%s%s%s%s" % (sys.argv[0],
+                 main.getUsage(),
+                 msgmod.getUsage(),
+                 secmod.getUsage(),
+                 mibview.getUsage(),
+                 target.getUsage(),
+                 pdu.getWriteUsage())
+
+# Construct c/l interpreter for this app
+
+class Scanner(msgmod.MPScannerMixIn,
+              secmod.SMScannerMixIn,
+              mibview.MibViewScannerMixIn,
+              target.TargetScannerMixIn,
+              pdu.WritePduScannerMixIn,
+              main.MainScannerMixIn,
+              base.ScannerTemplate): pass
+
+class Parser(msgmod.MPParserMixIn,
+             secmod.SMParserMixIn,
+             mibview.MibViewParserMixIn,
+             target.TargetParserMixIn,
+             pdu.WritePduParserMixIn,
+             main.MainParserMixIn,
+             base.ParserTemplate): pass
+
+# Run SNMP engine
+
+def cbFun(snmpEngine, sendRequestHandle, errorIndication,
+          errorStatus, errorIndex, varBinds, cbCtx):
+    if errorIndication:
+        sys.stderr.write('%s\n' % errorIndication)
+    elif errorStatus:
+        sys.stderr.write(
+            '%s at %s\n' %
+            ( errorStatus.prettyPrint(),
+              errorIndex and varBinds[int(errorIndex)-1] or '?' )
+        )
+    else:
+        for oid, val in varBinds:
+            sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
+                    cbCtx['mibViewController'], oid, val
+                )
+            )
+
+snmpEngine = engine.SnmpEngine()
+
+try:
+    # Parse c/l into AST
+    ast = Parser().parse(
+        Scanner().tokenize(' '.join(sys.argv[1:]))
+    )
+
+    ctx = {}
+
+    # Apply configuration to SNMP entity
+    main.generator((snmpEngine, ctx), ast)
+    msgmod.generator((snmpEngine, ctx), ast)
+    secmod.generator((snmpEngine, ctx), ast)    
+    mibview.generator((snmpEngine, ctx), ast)
+    target.generator((snmpEngine, ctx), ast)
+    pdu.writePduGenerator((snmpEngine, ctx), ast)
+
+    cmdgen.SetCommandGenerator().sendVarBinds(
+        snmpEngine,
+        ctx['addrName'],
+        ctx.get('contextEngineId'), ctx.get('contextName', ''),
+        ctx['varBinds'],
+        cbFun, ctx
+    )
+
+    snmpEngine.transportDispatcher.runDispatcher()
+
+except KeyboardInterrupt:
+    sys.stderr.write('Shutting down...\n')
+
+except error.PySnmpError:
+    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
+    sys.exit(-1)
+
+except Exception:
+    sys.stderr.write('Process terminated: %s\n' % sys.exc_info()[1])
+    for line in traceback.format_exception(*sys.exc_info()):
+        sys.stderr.write(line.replace('\n', ';'))
+    sys.exit(-1)
diff --git a/tools/pysnmptranslate b/scripts/snmptranslate.py
similarity index 80%
rename from tools/pysnmptranslate
rename to scripts/snmptranslate.py
index 6e65536..c4e51ca 100755
--- a/tools/pysnmptranslate
+++ b/scripts/snmptranslate.py
@@ -1,14 +1,17 @@
 #!/usr/bin/env python
 #
-# Command-line MIB browser
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
 #
-# Copyright 1999-2012 by Ilya Etingof <ilya@glas.net>.
+# Command-line MIB browser
 #
-import sys
+import sys, traceback
 from pyasn1.type import univ
 from pysnmp.entity import engine
 from pysnmp.proto import rfc3412
-from pysnmp.smi import builder, instrum
+from pysnmp.smi import builder
 from pysnmp_apps.cli import main, pdu, mibview, base
 from pysnmp.smi.error import NoSuchObjectError
 from pysnmp import error
@@ -24,31 +27,25 @@ TRANSLATE options:\n\
               l:  enable labeled OID report\n\
               o:  enable OID report\n\
               s:  enable dotted symbolic report\n\
-%s\n" % (
-        sys.argv[0],
-        main.getUsage(),
-        mibview.getUsage(),
-        pdu.getReadUsage()
-        )
+%s\n" % (sys.argv[0],
+         main.getUsage(),
+         mibview.getUsage(),
+         pdu.getReadUsage())
 
 # Construct c/l interpreter for this app
 
-class Scanner(
-    mibview.MibViewScannerMixIn,
-    pdu.ReadPduScannerMixIn,
-    main.MainScannerMixIn,
-    base.ScannerTemplate
-    ):
+class Scanner(mibview.MibViewScannerMixIn,
+              pdu.ReadPduScannerMixIn,
+              main.MainScannerMixIn,
+              base.ScannerTemplate):
     def t_transopts(self, s):
         r' -T '
         self.rv.append(base.ConfigToken('transopts'))
 
-class Parser(
-    mibview.MibViewParserMixIn,
-    pdu.ReadPduParserMixIn,
-    main.MainParserMixIn,
-    base.ParserTemplate
-    ):
+class Parser(mibview.MibViewParserMixIn,
+             pdu.ReadPduParserMixIn,
+             main.MainParserMixIn,
+             base.ParserTemplate):
     def p_transOptions(self, args):
         '''
         Cmdline ::= Options whitespace Params
@@ -110,7 +107,7 @@ class MibViewProxy(mibview.MibViewProxy):
         modName, nodeDesc, _suffix = mibViewController.getNodeLocation(prefix)
         mibNode, = mibViewController.mibBuilder.importSymbols(
             modName, nodeDesc
-            )        
+        )        
         out = ''
         if self.translateFullDetails:
             if suffix:
@@ -118,7 +115,7 @@ class MibViewProxy(mibview.MibViewProxy):
                 out = out + ' [ %s ]' % '.'.join([ str(x) for x in suffix ])
                 out = out + '\n'
             else:
-                out = out + '%s::%s %s\n::= { %s }' % (
+                out = out + '%s::%s\n%s ::= { %s }' % (
                     modName,
                     nodeDesc,
                     mibNode.asn1Print(),
@@ -129,14 +126,14 @@ class MibViewProxy(mibview.MibViewProxy):
         elif self.translateTrivial:
             out = '%s ::= { %s %s' % (
                 len(label) > 1 and label[-2] or ".", label[-1], prefix[-1]
-                )
+            )
             if suffix:
                 out = out + ' [ %s ]' % '.'.join([ str(x) for x in suffix ])
             out = out + ' }'
         elif self.translateLabeledOid:
             out = '.' + '.'.join(
                 map(lambda x,y: '%s(%s)' % (y, x),  prefix, label)
-                )
+            )
             if suffix:
                 out = out + ' [ %s ]' % '.'.join([ str(x) for x in suffix ])
         elif self.translateNumericOid:
@@ -150,23 +147,20 @@ class MibViewProxy(mibview.MibViewProxy):
         if not out:
             out = mibview.MibViewProxy.getPrettyOidVal(
                 self, mibViewController, oid, self._null
-                )
+            )
         return out
 
-mibInstrumController = instrum.MibInstrumController(
-    builder.MibBuilder()
-    )
+snmpEngine = engine.SnmpEngine()
+
 # Load up MIB texts (DESCRIPTION, etc.)
-mibInstrumController.mibBuilder.loadTexts = 1
-snmpEngine = engine.SnmpEngine(
-    msgAndPduDsp=rfc3412.MsgAndPduDispatcher(mibInstrumController)
-    )
+mibBuilder = snmpEngine.getMibBuilder()
+mibBuilder.loadTexts = True
 
 try:
     # Parse c/l into AST
     ast = Parser().parse(
         Scanner().tokenize(' '.join(sys.argv[1:]))
-        )
+    )
 
     ctx = {}
 
@@ -177,21 +171,32 @@ try:
     pdu.readPduGenerator((snmpEngine, ctx), ast)
     generator((snmpEngine, ctx), ast)
 
+except KeyboardInterrupt:
+    sys.stderr.write('Shutting down...\n')
+
 except error.PySnmpError:
     sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
     sys.exit(-1)
 
+except Exception:
+    sys.stderr.write('Process terminated: %s\n' % sys.exc_info()[1])
+    for line in traceback.format_exception(*sys.exc_info()):
+        sys.stderr.write(line.replace('\n', ';'))
+    sys.exit(-1)
+
+ctx['mibViewProxy'].buildValue = 0  # disable value printout
+
 for oid, val in ctx['varBinds']:
     while 1:
-        if val is None: val = univ.Null()
+        if val is None:
+            val = univ.Null()
         sys.stdout.write('%s\n' % ctx['mibViewProxy'].getPrettyOidVal(
-            ctx['mibViewController'], oid, val
-            ))
+                ctx['mibViewController'], oid, val
+            )
+        )
         if not ctx['mibViewProxy'].translateMassMode:
             break
         try:
             oid, label, suffix = ctx['mibViewController'].getNextNodeName(oid)
         except NoSuchObjectError:
             break
-        else:
-            sys.stdout.write('\n')
diff --git a/tools/pysnmptrap b/scripts/snmptrap.py
similarity index 60%
rename from tools/pysnmptrap
rename to scripts/snmptrap.py
index e448f83..78e96fa 100755
--- a/tools/pysnmptrap
+++ b/scripts/snmptrap.py
@@ -1,15 +1,17 @@
 #!/usr/bin/env python
 #
-# TRAP generator
+# This file is part of pysnmp-apps software.
 #
-# Copyright 1999-2012 by Ilya Etingof <ilya@glas.net>.
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
 #
-import sys, socket, time
+# Notificaton Originator
+#
+import sys, socket, time, traceback
 from pysnmp_apps.cli import main, msgmod, secmod, target, pdu, mibview, base
 from pysnmp.entity import engine, config
-from pysnmp.entity.rfc3413 import ntforg, context
+from pysnmp.entity.rfc3413 import ntforg
 from pysnmp.proto.proxy import rfc2576
-from pysnmp.proto import rfc1902
 from pysnmp.proto.api import v1, v2c
 from pysnmp import error
 
@@ -26,27 +28,23 @@ SNMPv1 TRAP management parameters:\n\
               generic-trap:         coldStart|warmStart|linkDown|linkUp|authenticationFailure|egpNeighborLoss|enterpriseSpecific\n\
 SNMPv2/SNMPv3 management parameters:\n\
    uptime trap-oid <management-params>\n\
-%s" % (
-        sys.argv[0],
-        main.getUsage(),
-        msgmod.getUsage(),
-        secmod.getUsage(),
-        mibview.getUsage(),
-        target.getUsage(),
-        pdu.getWriteUsage()
-        )
+%s" % (sys.argv[0],
+       main.getUsage(),
+       msgmod.getUsage(),
+       secmod.getUsage(),
+       mibview.getUsage(),
+       target.getUsage(),
+       pdu.getWriteUsage())
 
 # Construct c/l interpreter for this app
 
-class Scanner(
-    msgmod.MPScannerMixIn,
-    secmod.SMScannerMixIn,
-    mibview.MibViewScannerMixIn,
-    target.TargetScannerMixIn,
-    pdu.ReadPduScannerMixIn,
-    main.MainScannerMixIn,
-    base.ScannerTemplate
-    ):
+class Scanner(msgmod.MPScannerMixIn,
+              secmod.SMScannerMixIn,
+              mibview.MibViewScannerMixIn,
+              target.TargetScannerMixIn,
+              pdu.ReadPduScannerMixIn,
+              main.MainScannerMixIn,
+              base.ScannerTemplate):
     def t_appopts(self, s):
         r' -C '
         self.rv.append(base.ConfigToken('appopts'))
@@ -55,15 +53,13 @@ class Scanner(
         r' coldStart|warmStart|linkDown|linkUp|authenticationFailure|egpNeighborLoss|enterpriseSpecific '
         self.rv.append(base.ConfigToken('genericTrap', s))
         
-class Parser(
-    msgmod.MPParserMixIn,
-    secmod.SMParserMixIn,
-    mibview.MibViewParserMixIn,
-    target.TargetParserMixIn,
-    pdu.WritePduParserMixIn,
-    main.MainParserMixIn,
-    base.ParserTemplate
-    ):
+class Parser(msgmod.MPParserMixIn,
+             secmod.SMParserMixIn,
+             mibview.MibViewParserMixIn,
+             target.TargetParserMixIn,
+             pdu.WritePduParserMixIn,
+             main.MainParserMixIn,
+             base.ParserTemplate):
     def p_trapParams(self, args):
         '''
         TrapV1Params ::= EnterpriseOid whitespace AgentName whitespace GenericTrap whitespace SpecificTrap whitespace Uptime whitespace VarBinds
@@ -129,13 +125,12 @@ class __Generator(base.GeneratorTemplate):
         snmpEngine, ctx = cbCtx
         ctx['Uptime'] = int(node[0].attr)
 
+    def n_TrapOid(self, cbCtx, node):
+        snmpEngine, ctx = cbCtx
+        ctx['TrapOid'] = node[0].attr
+
     def n_TrapV1Params_exit(self, cbCtx, node):
         snmpEngine, ctx = cbCtx
-        # Hack SNMP engine's uptime
-        sysUpTime, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
-            '__SNMPv2-MIB', 'sysUpTime'
-            )
-        sysUpTime.syntax.createdAt = time.time() - float(ctx['Uptime'])/100
         # Initialize v1 PDU with passed params, then proxy it into v2c PDU
         v1Pdu = v1.TrapPDU()
         v1.apiTrapPDU.setDefaults(v1Pdu)
@@ -149,38 +144,57 @@ class __Generator(base.GeneratorTemplate):
             v1.apiTrapPDU.setSpecificTrap(v1Pdu, ctx['SpecificTrap'])
         if 'Uptime' in ctx:
             v1.apiTrapPDU.setTimeStamp(v1Pdu, ctx['Uptime'])
-        v2cPdu = rfc2576.v1ToV2(v1Pdu)
-
-        # Drop first two var-binds of v2c PDU as they are set internally by
-        # SNMP engine
-        varBinds = v2c.apiPDU.getVarBinds(v2cPdu)
-        if 'varBinds' not in ctx:
-            ctx['varBinds'] = []        
-        ctx['varBinds'] = varBinds[2:] + ctx['varBinds']
-        # Extract TrapOid from proxied PDU
-        ctx['TrapOid' ] = varBinds[1][1].prettyPrint()
-        
-    def n_TrapOid(self, cbCtx, node):
-        snmpEngine, ctx = cbCtx
-        ctx['TrapOid'] = node[0].attr
+        ctx['pdu'] = rfc2576.v1ToV2(v1Pdu)
 
     def n_TrapV2cParams_exit(self, cbCtx, node):
         snmpEngine, ctx = cbCtx
-        # Hack SNMP engine's uptime
-        sysUpTime, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMPv2-MIB', 'sysUpTime')
-        sysUpTime.syntax.createdAt = time.time() - float(ctx['Uptime'])/100
+        if 'informMode' in ctx:
+            pdu = v2c.InformRequestPDU()
+            v2c.apiPDU.setDefaults(pdu)
+        else:
+            pdu = v2c.TrapPDU()
+            v2c.apiTrapPDU.setDefaults(pdu)
+        v2c.apiPDU.setVarBinds(
+            pdu,
+           [ ( v2c.ObjectIdentifier('1.3.6.1.2.1.1.3.0'), v2c.TimeTicks(ctx['Uptime'])),
+             ( v2c.ObjectIdentifier('1.3.6.1.6.3.1.1.4.1.0'), v2c.ObjectIdentifier(ctx['TrapOid']) ) ]
+        )
+        ctx['pdu'] = pdu
         
 def generator(cbCtx, ast):
     snmpEngine, ctx = cbCtx
     return __Generator().preorder((snmpEngine, ctx), ast)
     
+# Run SNMP engine
+
+def cbFun(snmpEngine, notificationHandle, errorIndication, pdu, cbCtx):
+    if errorIndication:
+        sys.stderr.write('%s\n' % errorIndication)
+        return
+
+    errorStatus = v2c.apiPDU.getErrorStatus(pdu)
+    if errorStatus:
+        errorIndex = v2c.apiPDU.getErrorIndex(pdu)
+        sys.stderr.write(
+            '%s at %s\n' %
+            ( errorStatus.prettyPrint(),
+              errorIndex and varBinds[int(errorIndex)-1] or '?' )
+        )
+        return
+
+    for oid, val in v2c.apiPDU.getVarBinds(pdu):
+        sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
+                cbCtx['mibViewController'], oid, val
+            )
+        )
+        
 snmpEngine = engine.SnmpEngine()
 
 try:
     # Parse c/l into AST
     ast = Parser().parse(
         Scanner().tokenize(' '.join(sys.argv[1:]))
-        )
+    )
 
     ctx = {}
 
@@ -193,57 +207,30 @@ try:
     pdu.writePduGenerator((snmpEngine, ctx), ast)
     generator((snmpEngine, ctx), ast)
 
-except error.PySnmpError:
-    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
-    sys.exit(-1)
-
-# Run SNMP engine
-
-def cbFun(sendRequestHandle, errorIndication, cbCtx):
-    if errorIndication:
-        sys.stderr.write('%s\n' % errorIndication)
-        return
-
-snmpContext = context.SnmpContext(snmpEngine)
-
-# Agent-side VACM setup
-config.addContext(snmpEngine, '')
-config.addVacmUser(snmpEngine, 1, ctx['securityName'],
-                   'noAuthNoPriv', (), (), (1,3,6),
-                   contextName=ctx.get('contextName', '')) # v1
-config.addVacmUser(snmpEngine, 2, ctx['securityName'],
-                   'noAuthNoPriv', (), (), (1,3,6),
-                   contextName=ctx.get('contextName', '')) # v2c
-config.addVacmUser(snmpEngine, 3, ctx['securityName'],
-                   'authPriv', (), (), (1,3,6),
-                   contextName=ctx.get('contextName', '')) # v3
-config.addVacmUser(snmpEngine, 3, ctx['securityName'],
-                   'authNoPriv', (), (), (1,3,6),
-                   contextName=ctx.get('contextName', '')) # v3
-config.addVacmUser(snmpEngine, 3, ctx['securityName'],
-                   'noAuthNoPriv', (), (), (1,3,6),
-                   contextName=ctx.get('contextName', '')) # v3
-
-if 'contextName' in ctx:
-    snmpContext.registerContextName(
-        ctx.get('contextName', ''),
-        # ref to base MIB instrum
-        snmpEngine.msgAndPduDsp.mibInstrumController
-        )
-
-ctx['notificationName'] = 'myNotifyName'
-config.addNotificationTarget(
-    snmpEngine, ctx['notificationName'], ctx['paramsName'],
-    ctx['transportTag'], 'informMode' in ctx and 'inform' or 'trap'
+    v2c.apiPDU.setVarBinds(
+        ctx['pdu'], v2c.apiPDU.getVarBinds(ctx['pdu']) + ctx['varBinds']
     )
 
-ntforg.NotificationOriginator(snmpContext).sendNotification(
-    snmpEngine, ctx['notificationName'], ctx['TrapOid'],
-    ctx['varBinds'], cbFun, ctx, ctx.get('contextName', '')
+    ntforg.NotificationOriginator().sendPdu(
+        snmpEngine,
+        ctx['addrName'],
+        ctx.get('contextEngineId'),
+        ctx.get('contextName', ''),
+        ctx['pdu'],
+        cbFun, ctx
     )
 
-try:
     snmpEngine.transportDispatcher.runDispatcher()
+
+except KeyboardInterrupt:
+    sys.stderr.write('Shutting down...\n')
+
 except error.PySnmpError:
-    sys.stderr.write('Error: %s\n' % sys.exc_info()[1])
+    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
+    sys.exit(-1)
+
+except Exception:
+    sys.stderr.write('Process terminated: %s\n' % sys.exc_info()[1])
+    for line in traceback.format_exception(*sys.exc_info()):
+        sys.stderr.write(line.replace('\n', ';'))
     sys.exit(-1)
diff --git a/tools/pysnmpwalk b/scripts/snmpwalk.py
similarity index 63%
rename from tools/pysnmpwalk
rename to scripts/snmpwalk.py
index 8234feb..ba944aa 100755
--- a/tools/pysnmpwalk
+++ b/scripts/snmpwalk.py
@@ -1,10 +1,13 @@
 #!/usr/bin/env python
 #
-# GETNEXT command generator
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
 #
-# Copyright 1999-2012 by Ilya Etingof <ilya@glas.net>.
+# GETNEXT command generator
 #
-import sys, time
+import sys, time, traceback
 from pysnmp_apps.cli import main, msgmod, secmod, target, pdu, mibview, base
 from pysnmp.entity import engine
 from pysnmp.entity.rfc3413 import cmdgen
@@ -18,40 +21,35 @@ GETNEXT options:\n\
    -C<NEXTOPT>    set various application specific behaviours:\n\
               c:  do not check returned OIDs are increasing\n\
               t:  display wall-clock time to complete the request\n\
-              p:  print the number of variables found\n" % (
-        sys.argv[0],
-        main.getUsage(),
-        msgmod.getUsage(),
-        secmod.getUsage(),
-        mibview.getUsage(),
-        target.getUsage(),
-        pdu.getReadUsage()
-        )
+              p:  print the number of variables found\n\
+" % (sys.argv[0],
+     main.getUsage(),
+     msgmod.getUsage(),
+     secmod.getUsage(),
+     mibview.getUsage(),
+     target.getUsage(),
+     pdu.getReadUsage())
 
 # Construct c/l interpreter for this app
 
-class Scanner(
-    msgmod.MPScannerMixIn,
-    secmod.SMScannerMixIn,
-    mibview.MibViewScannerMixIn,
-    target.TargetScannerMixIn,
-    pdu.ReadPduScannerMixIn,
-    main.MainScannerMixIn,
-    base.ScannerTemplate
-    ):
+class Scanner(msgmod.MPScannerMixIn,
+              secmod.SMScannerMixIn,
+              mibview.MibViewScannerMixIn,
+              target.TargetScannerMixIn,
+              pdu.ReadPduScannerMixIn,
+              main.MainScannerMixIn,
+              base.ScannerTemplate):
     def t_appopts(self, s):
         r' -C '
         self.rv.append(base.ConfigToken('appopts'))
 
-class Parser(
-    msgmod.MPParserMixIn,
-    secmod.SMParserMixIn,
-    mibview.MibViewParserMixIn,
-    target.TargetParserMixIn,
-    pdu.ReadPduParserMixIn,
-    main.MainParserMixIn,
-    base.ParserTemplate
-    ):
+class Parser(msgmod.MPParserMixIn,
+             secmod.SMParserMixIn,
+             mibview.MibViewParserMixIn,
+             target.TargetParserMixIn,
+             pdu.ReadPduParserMixIn,
+             main.MainParserMixIn,
+             base.ParserTemplate):
     def p_appOptions(self, args):
         '''
         Option ::= ApplicationOption
@@ -81,35 +79,10 @@ def generator(cbCtx, ast):
     snmpEngine, ctx = cbCtx
     return __Generator().preorder((snmpEngine, ctx), ast)
 
-snmpEngine = engine.SnmpEngine()
-
-try:
-    # Parse c/l into AST
-    ast = Parser().parse(
-        Scanner().tokenize(' '.join(sys.argv[1:]))
-        )
-
-    ctx = {}
-
-    # Apply configuration to SNMP entity
-    main.generator((snmpEngine, ctx), ast)
-    msgmod.generator((snmpEngine, ctx), ast)
-    secmod.generator((snmpEngine, ctx), ast)    
-    mibview.generator((snmpEngine, ctx), ast)
-    target.generator((snmpEngine, ctx), ast)
-    pdu.readPduGenerator((snmpEngine, ctx), ast)
-    generator((snmpEngine, ctx), ast)
-    
-except error.PySnmpError:
-    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
-    sys.exit(-1)
-
-ctx['myHeadVars'] = [ rfc1902.ObjectName(x[0]) for x in ctx['varBinds'] ]
-
 # Run SNMP engine
 
-def cbFun(sendRequestHandle, errorIndication, errorStatus, errorIndex,
-          varBindTable, cbCtx):
+def cbFun(snmpEngine, sendRequestHandle, errorIndication,
+          errorStatus, errorIndex, varBindTable, cbCtx):
     if errorIndication:
         if errorIndication != 'oidNotIncreasing' or \
                not ctx.get('ignoreNonIncreasingOids'):
@@ -120,33 +93,66 @@ def cbFun(sendRequestHandle, errorIndication, errorStatus, errorIndex,
             '%s at %s\n' %
             ( errorStatus.prettyPrint(),
               errorIndex and varBindTable[0][int(errorIndex)-1] or '?' )
-            )
+        )
         return
     for varBindRow in varBindTable:
         colIdx = -1; inTableFlag = 0
         for oid, val in varBindRow:
-            colIdx = colIdx + 1
-            sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
-                cbCtx['mibViewController'], oid, val
-                ))
+            colIdx += 1
             if cbCtx['myHeadVars'][colIdx].isPrefixOf(oid):
-                inTableFlag = 1
+                sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
+                        cbCtx['mibViewController'], oid, val
+                    )
+                )
+                inTableFlag += 1
         if cbCtx.get('reportFoundVars'):
-            cbCtx['reportFoundVars'] = cbCtx['reportFoundVars'] + len(varBindRow)
+            cbCtx['reportFoundVars'] += inTableFlag
         if not inTableFlag:
             return # stop on end-of-table
     return 1 # continue walking
 
+snmpEngine = engine.SnmpEngine()
 
-cmdgen.NextCommandGenerator().sendReq(
-    snmpEngine, ctx['addrName'], ctx['varBinds'], cbFun, ctx,
-    ctx.get('contextEngineId'), ctx.get('contextName', '')
+try:
+    # Parse c/l into AST
+    ast = Parser().parse(
+        Scanner().tokenize(' '.join(sys.argv[1:]))
+    )
+
+    ctx = {}
+
+    # Apply configuration to SNMP entity
+    main.generator((snmpEngine, ctx), ast)
+    msgmod.generator((snmpEngine, ctx), ast)
+    secmod.generator((snmpEngine, ctx), ast)    
+    mibview.generator((snmpEngine, ctx), ast)
+    target.generator((snmpEngine, ctx), ast)
+    pdu.readPduGenerator((snmpEngine, ctx), ast)
+    generator((snmpEngine, ctx), ast)
+    
+    ctx['myHeadVars'] = [ rfc1902.ObjectName(x[0]) for x in ctx['varBinds'] ]
+
+    cmdgen.NextCommandGenerator().sendVarBinds(
+        snmpEngine,
+        ctx['addrName'],
+        ctx.get('contextEngineId'), ctx.get('contextName', ''),
+        ctx['varBinds'],
+        cbFun, ctx
     )
 
-try:
     snmpEngine.transportDispatcher.runDispatcher()
+
+except KeyboardInterrupt:
+    sys.stderr.write('Shutting down...\n')
+
 except error.PySnmpError:
-    sys.stderr.write('Error: %s\n' % sys.exc_info()[1])
+    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
+    sys.exit(-1)
+
+except Exception:
+    sys.stderr.write('Process terminated: %s\n' % sys.exc_info()[1])
+    for line in traceback.format_exception(*sys.exc_info()):
+        sys.stderr.write(line.replace('\n', ';'))
     sys.exit(-1)
 
 if ctx.get('reportFoundVars'):
diff --git a/setup.cfg b/setup.cfg
index 861a9f5..6f08d0e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,6 @@
+[bdist_wheel]
+universal = 1
+
 [egg_info]
 tag_build = 
 tag_date = 0
diff --git a/setup.py b/setup.py
index 54f0668..6403424 100644
--- a/setup.py
+++ b/setup.py
@@ -1,56 +1,86 @@
 #!/usr/bin/env python
+#
+# This file is part of pysnmp-apps software.
+#
+# Copyright (c) 2005-2016, Ilya Etingof <ilya@glas.net>
+# License: http://pysnmp.sf.net/license.html
+#
+"""SNMP command-line tools
+
+   A collection of command-line tools for SNMP management purposes built
+   on top of PySNMP package.
+"""
 import sys
 
+classifiers = """\
+Development Status :: 5 - Production/Stable
+Environment :: Console
+Intended Audience :: Developers
+Intended Audience :: Education
+Intended Audience :: Information Technology
+Intended Audience :: System Administrators
+Intended Audience :: Telecommunications Industry
+License :: OSI Approved :: BSD License
+Natural Language :: English
+Operating System :: OS Independent
+Programming Language :: Python :: 2
+Programming Language :: Python :: 3
+Topic :: Communications
+Topic :: Software Development :: Libraries :: Python Modules
+Topic :: System :: Monitoring
+Topic :: System :: Networking :: Monitoring
+Topic :: Software Development :: Libraries :: Python Modules
+"""
+
 def howto_install_setuptools():
-    print("""Error: You need setuptools Python package!
+    print("""
+   Error: You need setuptools Python package!
+
+   It's very easy to install it, just type (as root on Linux):
 
-It's very easy to install it, just type (as root on Linux):
-   wget http://peak.telecommunity.com/dist/ez_setup.py
+   wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
    python ez_setup.py
+
+   Then you could make eggs from this package.
 """)
 
+if sys.version_info[:2] < (2, 4):
+    print("ERROR: this package requires Python 2.4 or later!")
+    sys.exit(1)
+
 try:
     from setuptools import setup
-    params = {
-        'install_requires': [ 'pysnmp>=4.2.2' ],
-        'zip_safe': True
-        }
+    params = {'install_requires': [ 'pysnmp>=4.3.0' ],
+              'zip_safe': True}
 except ImportError:
     for arg in sys.argv:
-        if arg.find('egg') != -1:
+        if 'egg' in arg:
             howto_install_setuptools()
             sys.exit(1)
     from distutils.core import setup
     params = {}
     if sys.version_info[:2] > (2, 4):
-        params['requires'] = [ 'pysnmp(>=4.2.2)' ]
+        params['requires'] = [ 'pysnmp(>=4.3.0)' ]
+
+doclines = [x.strip() for x in (__doc__ or '').split('\n') if x]
 
-params.update( {
-    'name': 'pysnmp-apps',
-    'version': '0.3.2',
-    'description': 'PySNMP applications',
-    'author': 'Ilya Etingof',
-    'author_email': 'ilya@glas.net',
-    'url': 'http://sourceforge.net/projects/pysnmp/',
-    'classifiers': [
-        'Development Status :: 5 - Production/Stable',
-        'Intended Audience :: System Administrators',
-        'Intended Audience :: Information Technology',
-        'Intended Audience :: Telecommunications Industry',
-        'Operating System :: OS Independent',
-        'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 3',
-        'Topic :: Communications',
-        'Topic :: System :: Monitoring',
-        'Topic :: System :: Networking :: Monitoring',
-        'License :: OSI Approved :: BSD License'
-        ],
-    'license': 'BSD',
-    'packages': [ 'pysnmp_apps', 'pysnmp_apps.cli' ],
-    'scripts': [ 'tools/pysnmpget', 'tools/pysnmpset',
-                 'tools/pysnmpwalk', 'tools/pysnmpbulkwalk',
-                 'tools/pysnmptrap', 'tools/pysnmptranslate' ]
-  } )
+params.update(
+    {'name': 'pysnmp-apps',
+     'version': open('pysnmp_apps/__init__.py').read().split('\'')[1],
+     'description': doclines[0],
+     'long_description': ' '.join(doclines[1:]),
+     'maintainer': 'Ilya Etingof <ilya@glas.net>',
+     'author': 'Ilya Etingof',
+     'author_email': 'ilya@glas.net',
+     'url': 'http://sourceforge.net/projects/pysnmp/',
+     'classifiers': [ x for x in classifiers.split('\n') if x ],
+     'platforms': ['any'],
+     'license': 'BSD',
+     'packages': ['pysnmp_apps', 'pysnmp_apps.cli'],
+     'scripts': ['scripts/snmpget.py', 'scripts/snmpset.py',
+                  'scripts/snmpwalk.py', 'scripts/snmpbulkwalk.py',
+                  'scripts/snmptrap.py', 'scripts/snmptranslate.py']}
+)
 
 if "py2exe" in sys.argv:
     import py2exe
@@ -63,8 +93,14 @@ if "py2exe" in sys.argv:
             'includes': [
                 'pysnmp.smi.mibs.*',
                 'pysnmp.smi.mibs.instances.*'
-                ]
-            }
+            ],
+            'bundle_files': 1,
+            'compressed': True
         }
+    }
+
+    params['zipfile'] = None
+
+    print("!!! Make sure your pysnmp/pyasn1 packages are NOT .egg'ed!!!")
 
 setup(**params)
diff --git a/tools/pysnmpget b/tools/pysnmpget
deleted file mode 100755
index 3ff3e49..0000000
--- a/tools/pysnmpget
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env python
-#
-# GET command generator
-#
-# Copyright 1999-2012 by Ilya Etingof <ilya@glas.net>.
-#
-import sys
-from pysnmp_apps.cli import main, msgmod, secmod, target, pdu, mibview, base
-from pysnmp.entity import engine
-from pysnmp.entity.rfc3413 import cmdgen
-from pysnmp import error
-
-def getUsage():
-    return "Usage: %s [OPTIONS] <AGENT> <PARAMETERS>\n\
-%s%s%s%s%s%s" % (
-        sys.argv[0],
-        main.getUsage(),
-        msgmod.getUsage(),
-        secmod.getUsage(),
-        mibview.getUsage(),
-        target.getUsage(),
-        pdu.getReadUsage()
-        )
-
-# Construct c/l interpreter for this app
-
-class Scanner(
-    msgmod.MPScannerMixIn,
-    secmod.SMScannerMixIn,
-    mibview.MibViewScannerMixIn,
-    target.TargetScannerMixIn,
-    pdu.ReadPduScannerMixIn,
-    main.MainScannerMixIn,
-    base.ScannerTemplate
-    ): pass
-
-class Parser(
-    msgmod.MPParserMixIn,
-    secmod.SMParserMixIn,
-    mibview.MibViewParserMixIn,
-    target.TargetParserMixIn,
-    pdu.ReadPduParserMixIn,
-    main.MainParserMixIn,
-    base.ParserTemplate
-    ): pass
-
-snmpEngine = engine.SnmpEngine()
-
-try:
-    # Parse c/l into AST
-    ast = Parser().parse(
-        Scanner().tokenize(' '.join(sys.argv[1:]))
-        )
-
-    ctx = {}
-
-    # Apply configuration to SNMP entity
-    main.generator((snmpEngine, ctx), ast)
-    msgmod.generator((snmpEngine, ctx), ast)
-    secmod.generator((snmpEngine, ctx), ast)    
-    mibview.generator((snmpEngine, ctx), ast)
-    target.generator((snmpEngine, ctx), ast)
-    pdu.readPduGenerator((snmpEngine, ctx), ast)
-
-except error.PySnmpError:
-    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
-    sys.exit(-1)
-    
-# Run SNMP engine
-
-def cbFun(sendRequestHandle, errorIndication, errorStatus, errorIndex,
-          varBinds, cbCtx):
-    if errorIndication:
-        sys.stderr.write('%s\n' % errorIndication)
-    elif errorStatus:
-        sys.stderr.write(
-            '%s at %s\n' %
-            ( errorStatus.prettyPrint(),
-              errorIndex and varBinds[int(errorIndex)-1] or '?' )
-            )
-    else:
-        for oid, val in varBinds:
-            sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
-                cbCtx['mibViewController'], oid, val
-                ))
-
-cmdgen.GetCommandGenerator().sendReq(
-    snmpEngine, ctx['addrName'], ctx['varBinds'], cbFun, ctx,
-    ctx.get('contextEngineId'), ctx.get('contextName', '')
-    )
-
-try:
-    snmpEngine.transportDispatcher.runDispatcher()
-except error.PySnmpError:
-    sys.stderr.write('Error: %s\n' % sys.exc_info()[1])
-    sys.exit(-1)
diff --git a/tools/pysnmpset b/tools/pysnmpset
deleted file mode 100755
index 733068d..0000000
--- a/tools/pysnmpset
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env python
-#
-# SET command generator
-#
-# Copyright 1999-2012 by Ilya Etingof <ilya@glas.net>.
-#
-import sys
-from pysnmp_apps.cli import main, msgmod, secmod, target, pdu, mibview, base
-from pysnmp.entity import engine
-from pysnmp.entity.rfc3413 import cmdgen
-from pysnmp import error
-
-def getUsage():
-    return "Usage: %s [OPTIONS] <AGENT> <PARAMETERS>\n\
-%s%s%s%s%s%s" % (
-        sys.argv[0],
-        main.getUsage(),
-        msgmod.getUsage(),
-        secmod.getUsage(),
-        mibview.getUsage(),
-        target.getUsage(),
-        pdu.getWriteUsage()
-        )
-
-# Construct c/l interpreter for this app
-
-class Scanner(
-    msgmod.MPScannerMixIn,
-    secmod.SMScannerMixIn,
-    mibview.MibViewScannerMixIn,
-    target.TargetScannerMixIn,
-    pdu.WritePduScannerMixIn,
-    main.MainScannerMixIn,
-    base.ScannerTemplate
-    ): pass
-
-class Parser(
-    msgmod.MPParserMixIn,
-    secmod.SMParserMixIn,
-    mibview.MibViewParserMixIn,
-    target.TargetParserMixIn,
-    pdu.WritePduParserMixIn,
-    main.MainParserMixIn,
-    base.ParserTemplate
-    ): pass
-
-snmpEngine = engine.SnmpEngine()
-
-try:
-    # Parse c/l into AST
-    ast = Parser().parse(
-        Scanner().tokenize(' '.join(sys.argv[1:]))
-        )
-
-    ctx = {}
-
-    # Apply configuration to SNMP entity
-    main.generator((snmpEngine, ctx), ast)
-    msgmod.generator((snmpEngine, ctx), ast)
-    secmod.generator((snmpEngine, ctx), ast)    
-    mibview.generator((snmpEngine, ctx), ast)
-    target.generator((snmpEngine, ctx), ast)
-    pdu.writePduGenerator((snmpEngine, ctx), ast)
-
-except error.PySnmpError:
-    sys.stderr.write('Error: %s\n%s' % (sys.exc_info()[1], getUsage()))
-    sys.exit(-1)
-    
-# Run SNMP engine
-
-def cbFun(sendRequestHandle, errorIndication, errorStatus, errorIndex,
-          varBinds, cbCtx):
-    if errorIndication:
-        sys.stderr.write('%s\n' % errorIndication)
-    elif errorStatus:
-        sys.stderr.write(
-            '%s at %s\n' %
-            ( errorStatus.prettyPrint(),
-              errorIndex and varBinds[int(errorIndex)-1] or '?' )
-            )
-    else:
-        for oid, val in varBinds:
-            sys.stdout.write('%s\n' % cbCtx['mibViewProxy'].getPrettyOidVal(
-                cbCtx['mibViewController'], oid, val
-                ))
-
-cmdgen.SetCommandGenerator().sendReq(
-    snmpEngine, ctx['addrName'], ctx['varBinds'], cbFun, ctx,
-    ctx.get('contextEngineId'), ctx.get('contextName', '')
-    )
-
-try:
-    snmpEngine.transportDispatcher.runDispatcher()
-except error.PySnmpError:
-    sys.stderr.write('Error: %s\n' % sys.exc_info()[1])
-    sys.exit(-1)

More details

Full run details