Hello community,

here is the log from the commit of package python-ciscoconfparse for 
openSUSE:Factory checked in at 2019-08-05 10:36:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-ciscoconfparse (Old)
 and      /work/SRC/openSUSE:Factory/.python-ciscoconfparse.new.4126 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-ciscoconfparse"

Mon Aug  5 10:36:51 2019 rev:7 rq:720165 version:1.4.1

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-ciscoconfparse/python-ciscoconfparse.changes  
    2019-03-28 22:48:26.995057314 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-ciscoconfparse.new.4126/python-ciscoconfparse.changes
    2019-08-05 10:36:54.403330845 +0200
@@ -1,0 +2,8 @@
+Thu Aug  1 08:10:40 UTC 2019 - [email protected]
+
+- version update to 1.4.1
+  * Fix Github issue #141; NOTE - This will break parsing helper-addresses 
under factory=True.  If you use this feature, please migrate your scripts to 
the new format
+  * Implement re_search_children() directly on the CiscoConfParse() object
+  *  Fix Github issue #158, *KEYWORD CHANGE WARNING:* some methods formrely 
used a keyword called 'all_children'.  The new syntax is 'recurse' (ref: Github 
issue #159)
+
+-------------------------------------------------------------------

Old:
----
  ciscoconfparse-1.3.32.tar.gz

New:
----
  ciscoconfparse-1.4.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-ciscoconfparse.spec ++++++
--- /var/tmp/diff_new_pack.2TbNbU/_old  2019-08-05 10:36:54.947330779 +0200
+++ /var/tmp/diff_new_pack.2TbNbU/_new  2019-08-05 10:36:54.951330779 +0200
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-ciscoconfparse
-Version:        1.3.32
+Version:        1.4.1
 Release:        0
 Summary:        Library for parsing, querying and modifying Cisco IOS-style 
configurations
 License:        GPL-3.0-or-later
@@ -27,6 +27,9 @@
 Source:         
https://files.pythonhosted.org/packages/source/c/ciscoconfparse/ciscoconfparse-%{version}.tar.gz
 BuildRequires:  %{python_module colorama}
 BuildRequires:  %{python_module dnspython}
+BuildRequires:  %{python_module ipaddr}
+BuildRequires:  %{python_module mock}
+BuildRequires:  %{python_module passlib}
 BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
@@ -63,7 +66,8 @@
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check
-# Upstream stubbornly refuses PRs and does not distribute tests
+# https://github.com/mpenning/ciscoconfparse/issues/160
+# otherwise test passed for 1.4.1
 #%%pytest
 
 %files %{python_files}

++++++ ciscoconfparse-1.3.32.tar.gz -> ciscoconfparse-1.4.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/CHANGES 
new/ciscoconfparse-1.4.1/CHANGES
--- old/ciscoconfparse-1.3.32/CHANGES   2019-03-17 13:46:40.000000000 +0100
+++ new/ciscoconfparse-1.4.1/CHANGES    2019-07-28 15:17:56.000000000 +0200
@@ -1,3 +1,16 @@
+1.4.1   20190728 Implement re_search_children() directly on the 
CiscoConfParse() object
+1.4.0   20190727 Fix Github issue #158, *KEYWORD CHANGE WARNING:* some methods 
formrely used a keyword called 'all_children'.  The new syntax is 'recurse' 
(ref: Github issue #159)
+1.3.43  20190705 Attempt to fix Github issue #154
+1.3.42  20190627 Fix Github issue #153
+1.3.41  20190627 [RELEASE BROKEN] HSRP timers now return a float (used to 
return int).  NXOS support for HSRP (Github issue #152)
+1.3.40  20190620 
+1.3.39  20190527 Allow re_match_iter_typed() to use default=True
+1.3.38  20190527 Add CiscoConfParse().re_match_iter_typed()
+1.3.37  20190512 Take a step further towards full Python 3 compat (Github 
issue #98)
+1.3.36  20190422 Refine Junos parsing (Github issue #142)
+1.3.35  20190420 Refine IOS macro parsing (Github issue #144)
+1.3.34  20190419 Add support for IOS macros (Github issue #143)
+1.3.33  20190407 Merge Github PR #140 (delete_lines() bug)
 1.3.32  20190317 Fix Github issue #135
 1.3.31  20190316 Fix Github issues #131, 132, 133, 134
 1.3.30  20190218 Fix bugs related to Python3 (Github issue #117)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/PKG-INFO 
new/ciscoconfparse-1.4.1/PKG-INFO
--- old/ciscoconfparse-1.3.32/PKG-INFO  2019-03-17 13:53:07.000000000 +0100
+++ new/ciscoconfparse-1.4.1/PKG-INFO   2019-07-28 15:36:01.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: ciscoconfparse
-Version: 1.3.32
+Version: 1.4.1
 Summary: Parse, Audit, Query, Build, and Modify Cisco IOS-style configurations
 Home-page: http://www.pennington.net/py/ciscoconfparse/
 Author: David Michael Pennington
@@ -82,34 +82,33 @@
         Pre-requisites
         ==============
         
-        As of version 1.3.32, ciscoconfparse_ requires Python versions 2.7 or 
3.4+ 
-        (note: version 3.7.0 has a bug - ref Github issue #117, but version 
3.7.1 
-        works); the OS should not matter.
+        ciscoconfparse_ requires Python versions 2.7 or 3.4+ (note: version 
3.7.0 has 
+        a bug - ref Github issue #117, but version 3.7.1 works); the OS should 
not 
+        matter.
         
         .. _Installation:
         
         Installation and Downloads
         ==========================
         
-        The best way to get ciscoconfparse is with setuptools_ or pip_.  If 
you 
-        already have setuptools_, you can install as usual:
+        You can install into Python2.x with pip_:
         
         ::
         
-              # Substitute whatever ciscoconfparse version you like...
-              easy_install -U ciscoconfparse==1.3.32
+              pip install --upgrade ciscoconfparse
         
-        Alternatively you can install into Python2.x with pip_:
+        Use ``pip3`` for Python3.x...
         
         ::
         
-              pip install --upgrade ciscoconfparse
+              pip3 install --upgrade ciscoconfparse
         
-        Use ``pip3`` for Python3.x...
+        If you don't want to use pip_, you can install with `easy_install`:
         
         ::
         
-              pip3 install --upgrade ciscoconfparse
+              easy_install -U ciscoconfparse
+        
         
         Otherwise `download it from PyPi 
<https://pypi.python.org/pypi/ciscoconfparse>`_, extract it and run the 
``setup.py`` script:
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/README.rst 
new/ciscoconfparse-1.4.1/README.rst
--- old/ciscoconfparse-1.3.32/README.rst        2019-03-17 13:45:26.000000000 
+0100
+++ new/ciscoconfparse-1.4.1/README.rst 2019-05-12 03:51:16.000000000 +0200
@@ -74,34 +74,33 @@
 Pre-requisites
 ==============
 
-As of version 1.3.32, ciscoconfparse_ requires Python versions 2.7 or 3.4+ 
-(note: version 3.7.0 has a bug - ref Github issue #117, but version 3.7.1 
-works); the OS should not matter.
+ciscoconfparse_ requires Python versions 2.7 or 3.4+ (note: version 3.7.0 has 
+a bug - ref Github issue #117, but version 3.7.1 works); the OS should not 
+matter.
 
 .. _Installation:
 
 Installation and Downloads
 ==========================
 
-The best way to get ciscoconfparse is with setuptools_ or pip_.  If you 
-already have setuptools_, you can install as usual:
+You can install into Python2.x with pip_:
 
 ::
 
-      # Substitute whatever ciscoconfparse version you like...
-      easy_install -U ciscoconfparse==1.3.32
+      pip install --upgrade ciscoconfparse
 
-Alternatively you can install into Python2.x with pip_:
+Use ``pip3`` for Python3.x...
 
 ::
 
-      pip install --upgrade ciscoconfparse
+      pip3 install --upgrade ciscoconfparse
 
-Use ``pip3`` for Python3.x...
+If you don't want to use pip_, you can install with `easy_install`:
 
 ::
 
-      pip3 install --upgrade ciscoconfparse
+      easy_install -U ciscoconfparse
+
 
 Otherwise `download it from PyPi 
<https://pypi.python.org/pypi/ciscoconfparse>`_, extract it and run the 
``setup.py`` script:
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/__init__.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/__init__.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/__init__.py        2019-02-19 
03:02:10.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/__init__.py 2019-05-12 
03:57:52.000000000 +0200
@@ -1,4 +1,5 @@
-from ciscoconfparse import *
+from __future__  import absolute_import
+from ciscoconfparse.ciscoconfparse import *
 
 """ __init__.py - Parse, Query, Build, and Modify IOS-style configurations
      Copyright (C) 2007-2015 David Michael Pennington
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/__main__.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/__main__.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/__main__.py        2019-02-19 
03:02:10.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/__main__.py 2019-07-05 
23:24:51.000000000 +0200
@@ -1,10 +1,6 @@
-# Follow PEP366...
-#     Credit (the question): http://stackoverflow.com/q/2943847/667301
-__package__ = "ciscoconfparse"
-from .ciscoconfparse import *
-
+from __future__  import absolute_import
 """ __main__.py - Parse, Query, Build, and Modify IOS-style configurations
-     Copyright (C) 2014-2015 David Michael Pennington
+     Copyright (C) 2014-2019 David Michael Pennington
 
      This program is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
@@ -22,3 +18,15 @@
      If you need to contact the author, you can do so by emailing:
      mike [~at~] pennington [/dot\] net
 """
+# Follow PEP366...
+# https://stackoverflow.com/a/6655098/667301
+if (__name__=='__main__') and (__package__ is None):
+    import sys
+    import os
+
+    parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+    sys.path.insert(1, parent_dir)
+    import ciscoconfparse
+    __package__ = str("ciscoconfparse")
+    del sys, os
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/ccp_abc.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/ccp_abc.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/ccp_abc.py 2019-03-16 
18:45:59.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/ccp_abc.py  2019-07-27 
22:04:39.000000000 +0200
@@ -1,10 +1,11 @@
+from __future__  import absolute_import
 from operator import methodcaller, attrgetter
 from abc import ABCMeta, abstractmethod
 from copy import deepcopy
 import re
 import os
 
-from ccp_util import IPv4Obj
+from ciscoconfparse.ccp_util import IPv4Obj
 
 r""" ccp_abc.py - Parse, Query, Build, and Modify IOS-style configurations
      Copyright (C) 2014-2015, 2019 David Michael Pennington
@@ -250,7 +251,7 @@
            ...     obj.delete_children_matching(r'description')
            >>>
            >>> for line in parse.ioscfg:
-           ...     print line
+           ...     print(line)
            ...
            !
            interface Serial1/0
@@ -320,7 +321,7 @@
            ...     obj.append_to_family(' carrier-delay msec 500')
            >>>
            >>> for line in parse.ioscfg:
-           ...     print line
+           ...     print(line)
            ...
            !
            interface Serial1/0
@@ -387,9 +388,9 @@
            >>> parse = CiscoConfParse(config)
            >>>
            >>> for obj in parse.find_objects('Serial'):
-           ...     print "OLD", obj.text
+           ...     print("OLD {}".format(obj.text))
            ...     obj.replace(r'Serial1', r'Serial0')
-           ...     print "  NEW", obj.text
+           ...     print("  NEW {}".format(obj.text))
            OLD interface Serial1/0
              NEW interface Serial0/0
            OLD interface Serial1/1
@@ -432,9 +433,9 @@
            >>> parse = CiscoConfParse(config)
            >>>
            >>> for obj in parse.find_objects('Serial'):
-           ...     print "OLD", obj.text
-           ...     obj.re_sub(r'Serial1', r'Serial0')
-           ...     print "  NEW", obj.text
+           ...     print("OLD {}".format(obj.text))
+           ...     obj.replace(r'Serial1', r'Serial0')
+           ...     print("  NEW {}".format(obj.text))
            OLD interface Serial1/0
              NEW interface Serial0/0
            OLD interface Serial1/1
@@ -489,7 +490,7 @@
            >>> for obj in parse.find_objects(r'ip\saddress'):
            ...     netmask = obj.re_match(r'1\.1\.1\.5\s(\S+)')
            >>>
-           >>> print "The netmask is", netmask
+           >>> print("The netmask is", netmask)
            The netmask is 255.255.255.252
            >>>
         """
@@ -517,18 +518,22 @@
             return self.text
         return default
 
-    def re_search_children(self, regex):
+    def re_search_children(self, regex, recurse=False):
         """Use ``regex`` to search the text contained in the children of 
         this :class:`~models_cisco.IOSCfgLine`.
 
         Args:
             - regex (str): A string or python regular expression, which should 
be matched.  
+            - recurse (bool): Set True if you want to search all children 
(children, grand children, great grand children, etc...)
 
         Returns:
             - list.  A list of matching :class:`~models_cisco.IOSCfgLine` 
objects which matched.  If there is no match, an empty :py:func:`list` is 
returned.
 
         """
-        return [cobj for cobj in self.children if cobj.re_search(regex)]
+        if recurse is False:
+            return [cobj for cobj in self.children if cobj.re_search(regex)]
+        else:
+            return [cobj for cobj in self.all_children if 
cobj.re_search(regex)]
 
     def re_match_typed(self, regex, group=1, untyped_default=False, 
         result_type=str, default=''):
@@ -546,7 +551,7 @@
             - untyped_default (bool): Set True if you don't want the default 
value to be typed
 
         Returns:
-            - ``result_type``.  The text matched by the regular expression 
group; if there is no match, ``default`` is returned.  All values are cast as 
``result_type``.
+            - ``result_type``.  The text matched by the regular expression 
group; if there is no match, ``default`` is returned.  All values are cast as 
``result_type``, unless `untyped_default` is True.
 
         This example illustrates how you can use 
         :func:`~models_cisco.IOSCfgLine.re_match_typed` to build an 
@@ -575,7 +580,7 @@
            ...     slot = obj.re_match_typed(regex=r'Serial(\d+)',
            ...         result_type=int,
            ...         default=-1)
-           ...     print "Interface {0} is in slot {1}".format(name, slot)
+           ...     print("Interface {0} is in slot {1}".format(name, slot))
            ...
            Interface Serial1/0 is in slot 1
            Interface Serial2/0 is in slot 2
@@ -593,7 +598,7 @@
             return result_type(default)
 
     def re_match_iter_typed(self, regex, group=1, result_type=str, default='', 
-        untyped_default=False, all_children=False):
+        untyped_default=False, recurse=False):
         r"""Use ``regex`` to search the children of 
         :class:`~models_cisco.IOSCfgLine` text and return the contents of 
         the regular expression group, at the integer ``group`` index, cast as 
@@ -605,11 +610,12 @@
             - group (int): An integer which specifies the desired regex group 
to be returned.  ``group`` defaults to 1.
             - result_type (type): A type (typically one of: ``str``, ``int``, 
``float``, or :class:`~ccp_util.IPv4Obj`).         All returned values are cast 
as ``result_type``, which defaults to ``str``.
             - default (any): The default value to be returned, if there is no 
match.
-            - all_children (bool): Set True if you want to search all children 
(children, grand children, great grand children, etc...)
+            - recurse (bool): Set True if you want to search all children 
(children, grand children, great grand children, etc...)
             - untyped_default (bool): Set True if you don't want the default 
value to be typed
 
         Returns:
-            - ``result_type``.  The text matched by the regular expression 
group; if there is no match, ``default`` is returned.  All values are cast as 
``result_type``.
+            - ``result_type``.  The text matched by the regular expression 
group; if there is no match, ``default`` is returned.  All values are cast as 
``result_type``, unless `untyped_default` is True.
+
         - NOTE: This loops through the children (in order) and returns when 
the regex hits its first match.
 
         This example illustrates how you can use 
@@ -631,7 +637,7 @@
            >>> INTF_RE = re.compile(r'interface\s\S+')
            >>> ADDR_RE = re.compile(r'ip\saddress\s(\S+\s+\S+)')
            >>> for obj in parse.find_objects(INTF_RE):
-           ...     print obj.text, obj.re_match_iter_typed(ADDR_RE, 
result_type=IPv4Obj)
+           ...     print("{} {}".format(obj.text, 
obj.re_match_iter_typed(ADDR_RE, result_type=IPv4Obj)))
            interface Serial1/0 <IPv4Obj 1.1.1.1/30>
            interface Serial2/0 <IPv4Obj 1.1.1.5/30>
            >>>
@@ -639,15 +645,15 @@
         ## iterate through children, and return the matching value 
         ##  (cast as result_type) from the first child.text that matches regex
 
-        if (default is True):
+        #if (default is True):
             ## Not using self.re_match_iter_typed(default=True), because I want
             ##   to be sure I build the correct API for match=False
             ##
             ## Ref IOSIntfLine.has_dtp for an example of how to code around
             ##   this while I build the API
-            raise NotImplementedError
+        #    raise NotImplementedError
 
-        if all_children is False:
+        if recurse is False:
             for cobj in self.children:
                 mm = re.search(regex, cobj.text)
                 if not (mm is None):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/ccp_util.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/ccp_util.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/ccp_util.py        2019-03-17 
13:42:33.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/ccp_util.py 2019-05-12 
03:58:07.000000000 +0200
@@ -1,3 +1,4 @@
+from __future__  import absolute_import
 import itertools
 import socket
 import time
@@ -11,7 +12,7 @@
     ## This syntax is not supported in Python 3...
     from collections import MutableSequence
 
-from protocol_values import ASA_TCP_PORTS, ASA_UDP_PORTS
+from ciscoconfparse.protocol_values import ASA_TCP_PORTS, ASA_UDP_PORTS
 from dns.exception import DNSException
 from dns.resolver import Resolver
 from dns import reversename, query, zone
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ciscoconfparse-1.3.32/ciscoconfparse/ciscoconfparse.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/ciscoconfparse.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/ciscoconfparse.py  2019-03-17 
13:50:43.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/ciscoconfparse.py   2019-07-28 
15:30:15.000000000 +0200
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
 from operator import methodcaller, attrgetter
 from colorama import Fore, Back, Style
 from difflib import SequenceMatcher
@@ -14,38 +15,39 @@
     from collections import MutableSequence, Iterator
 
 
-from models_cisco import IOSHostnameLine, IOSRouteLine, IOSIntfLine
-from models_cisco import IOSAccessLine, IOSIntfGlobal
-from models_cisco import IOSAaaLoginAuthenticationLine
-from models_cisco import IOSAaaEnableAuthenticationLine
-from models_cisco import IOSAaaCommandsAuthorizationLine
-from models_cisco import IOSAaaCommandsAccountingLine
-from models_cisco import IOSAaaExecAccountingLine
-from models_cisco import IOSAaaGroupServerLine
-from models_cisco import IOSCfgLine
-
-from models_nxos import NXOSHostnameLine, NXOSRouteLine, NXOSIntfLine
-from models_nxos import NXOSAccessLine, NXOSIntfGlobal
-from models_nxos import NXOSAaaLoginAuthenticationLine
-from models_nxos import NXOSAaaEnableAuthenticationLine
-from models_nxos import NXOSAaaCommandsAuthorizationLine
-from models_nxos import NXOSAaaCommandsAccountingLine
-from models_nxos import NXOSAaaExecAccountingLine
-from models_nxos import NXOSAaaGroupServerLine
-from models_nxos import NXOSCfgLine
-
-from models_asa import ASAObjGroupNetwork
-from models_asa import ASAObjGroupService
-from models_asa import ASAHostnameLine
-from models_asa import ASAObjNetwork
-from models_asa import ASAObjService
-from models_asa import ASAIntfGlobal
-from models_asa import ASAIntfLine
-from models_asa import ASACfgLine
-from models_asa import ASAName
-from models_asa import ASAAclLine
+from ciscoconfparse.models_cisco import IOSHostnameLine, IOSRouteLine
+from ciscoconfparse.models_cisco import IOSIntfLine
+from ciscoconfparse.models_cisco import IOSAccessLine, IOSIntfGlobal
+from ciscoconfparse.models_cisco import IOSAaaLoginAuthenticationLine
+from ciscoconfparse.models_cisco import IOSAaaEnableAuthenticationLine
+from ciscoconfparse.models_cisco import IOSAaaCommandsAuthorizationLine
+from ciscoconfparse.models_cisco import IOSAaaCommandsAccountingLine
+from ciscoconfparse.models_cisco import IOSAaaExecAccountingLine
+from ciscoconfparse.models_cisco import IOSAaaGroupServerLine
+from ciscoconfparse.models_cisco import IOSCfgLine
+
+from ciscoconfparse.models_nxos import NXOSHostnameLine, NXOSRouteLine, 
NXOSIntfLine
+from ciscoconfparse.models_nxos import NXOSAccessLine, NXOSIntfGlobal
+from ciscoconfparse.models_nxos import NXOSAaaLoginAuthenticationLine
+from ciscoconfparse.models_nxos import NXOSAaaEnableAuthenticationLine
+from ciscoconfparse.models_nxos import NXOSAaaCommandsAuthorizationLine
+from ciscoconfparse.models_nxos import NXOSAaaCommandsAccountingLine
+from ciscoconfparse.models_nxos import NXOSAaaExecAccountingLine
+from ciscoconfparse.models_nxos import NXOSAaaGroupServerLine
+from ciscoconfparse.models_nxos import NXOSCfgLine
+
+from ciscoconfparse.models_asa import ASAObjGroupNetwork
+from ciscoconfparse.models_asa import ASAObjGroupService
+from ciscoconfparse.models_asa import ASAHostnameLine
+from ciscoconfparse.models_asa import ASAObjNetwork
+from ciscoconfparse.models_asa import ASAObjService
+from ciscoconfparse.models_asa import ASAIntfGlobal
+from ciscoconfparse.models_asa import ASAIntfLine
+from ciscoconfparse.models_asa import ASACfgLine
+from ciscoconfparse.models_asa import ASAName
+from ciscoconfparse.models_asa import ASAAclLine
 
-from models_junos import JunosCfgLine
+from ciscoconfparse.models_junos import JunosCfgLine
 
 r""" ciscoconfparse.py - Parse, Query, Build, and Modify IOS-style configs
      Copyright (C) 2007-2019 David Michael Pennington
@@ -72,7 +74,8 @@
     'version')
 # __version__ if-else below fixes Github issue #123
 if os.path.isfile(versionfilepath):
-    __version__ = open(versionfilepath).read().strip()
+    with open(versionfilepath) as vh:
+        __version__ = vh.read().strip()
 else:
     # This case is required for importing from a zipfile... Github issue #123
     __version__ = "0.0.0"  # __version__ read failed
@@ -114,7 +117,7 @@
                - factory (bool): ``factory`` defaults to False; if set 
``True``, it enables a beta-quality configuration line classifier.
                - linesplit_rgx (str): ``linesplit_rgx`` is used when parsing 
configuration files to find where new configuration lines are.  It is best to 
leave this as the default, unless you're working on a system that uses unusual 
line terminations (for instance something besides Unix, OSX, or Windows)
                - ignore_blank_lines (bool): ``ignore_blank_lines`` defaults to 
True; when this is set True, ciscoconfparse ignores blank configuration lines.  
You might want to set ``ignore_blank_lines`` to False if you intentionally use 
blank lines in your configuration (ref: Github Issue #2), or you are parsing 
configurations which naturally have blank lines (such as Cisco Nexus 
configurations).
-               - syntax (str): ``syntax`` defaults to 'ios'; You can choose 
from the following values: ios, nxos, asa
+               - syntax (str): ``syntax`` defaults to 'ios'; You can choose 
from the following values: ios, nxos, asa, junos
 
            Attributes:
                - comment_delimiter (str): A string containing the 
comment-delimiter
@@ -374,6 +377,7 @@
         (?:\s*
            (?:(?P<line>[^\{{\}}{0}].*?)(?P<braces_eol>[\{{\}}])*(?P<sc>\;)*\s*)
           |(?P<braces_alone>[\{{\}}\;])
+          |(?P<brace_sc>\}}\;)\s*
           |(?P<junos_else>^\s*\}}\s*else\s*\{{\s*$)
           |(?:\s*[{0}](?P<comment>.*))
         )
@@ -397,12 +401,15 @@
 
                 junos_else = results.get('junos_else', None)
                 term_char = (results['braces_eol'] or
+                             results.get('braces_sc', None) or
                              results['braces_alone'] or '').strip()
                 comment = results['comment']
                 if term_char == '{':
                     level_offset = 1
                 elif term_char == '}':
                     level_offset = -1
+                elif term_char == '};':
+                    level_offset = -1
 
                 ## Return values
                 if comment is not None:
@@ -416,17 +423,20 @@
                 ## pass blank lines back
                 return input, 0
             else:
-                raise ValueError("Could not parse: '{0}'".format(input))
+                raise ValueError("LINE_RE Regex fail - Could not parse: 
'{0}'".format(input))
 
         lines = list()
         offset = 0
         STOP_WIDTH = stop_width
-        for tmp in input_list:
+        for idx, tmp in enumerate(input_list):
+            if self.debug is True:
+                _log.debug("Parse line {0}:'{1}'".format(idx+1, tmp.strip()))
             line, line_offset = line_level(tmp.strip())
+            if line is None:
+                line = ""
             # Debugging here...
             #print "FOO", tmp, "BAR", line, line_offset
-            if line:
-                lines.append(" " * STOP_WIDTH * offset + line)
+            lines.append(" " * STOP_WIDTH * offset + line)
             offset += line_offset
         return lines
 
@@ -449,7 +459,7 @@
             - list.  A list of matching :class:`~ciscoconfparse.IOSIntfLine` 
objects
 
         .. code-block:: python
-           :emphasize-lines: 12,15
+           :emphasize-lines: 12
 
            >>> config = [
            ...     '!',
@@ -868,7 +878,8 @@
 
         return list(map(attrgetter('text'), sorted(tmp)))
 
-    def find_objects_w_child(self, parentspec, childspec, ignore_ws=False):
+    def find_objects_w_child(self, parentspec, childspec, ignore_ws=False,
+        recurse=False):
         """Return a list of parent :class:`~models_cisco.IOSCfgLine` objects, 
         which matched the ``parentspec`` and whose children match 
``childspec``.
         Only the parent :class:`~models_cisco.IOSCfgLine` objects will be 
@@ -879,6 +890,7 @@
             - childspec (str): Text regular expression for the line to be 
matched; this must match the child's line
         Kwargs:
             - ignore_ws (bool): boolean that controls whether whitespace is 
ignored
+            - recurse (bool): Set True if you want to search all children 
(children, grand children, great grand children, etc...)
 
         Returns:
             - list.  A list of matching parent 
:class:`~models_cisco.IOSCfgLine` objects
@@ -898,7 +910,7 @@
             switchport access vlan 300
             spanning-tree portfast
            !
-           interface FastEthernet0/2
+           interface FastEthernet0/3
             duplex full
             speed 100
             switchport access vlan 300
@@ -948,13 +960,14 @@
             childspec = self._build_space_tolerant_regex(childspec)
 
         return list(
-            filter(lambda x: x.re_search_children(childspec),
-                   self.find_objects(parentspec)))
+            filter(lambda x: x.re_search_children(childspec, 
+                    recurse=recurse), self.find_objects(parentspec)))
 
     def find_objects_w_all_children(self,
                                     parentspec,
                                     childspec,
-                                    ignore_ws=False):
+                                    ignore_ws=False,
+                                    recurse=False):
         """Return a list of parent :class:`~models_cisco.IOSCfgLine` objects, 
         which matched the ``parentspec`` and whose children match all elements
         in ``childspec``.  Only the parent :class:`~models_cisco.IOSCfgLine` 
@@ -965,6 +978,7 @@
             - childspec (str): A list of text regular expressions to be 
matched among the children
         Kwargs:
             - ignore_ws (bool): boolean that controls whether whitespace is 
ignored
+            - recurse (bool): Set True if you want to search all children 
(children, grand children, great grand children, etc...)
 
         Returns:
             - list.  A list of matching parent 
:class:`~models_cisco.IOSCfgLine` objects
@@ -1038,7 +1052,8 @@
         for parentobj in self.find_objects(parentspec):
             results = set([])
             for child_cfg in childspec:
-                results.add(bool(parentobj.re_search_children(child_cfg)))
+                results.add(bool(parentobj.re_search_children(child_cfg,
+                    recurse=recurse)))
             if False in results:
                 continue
             else:
@@ -1050,6 +1065,19 @@
                                         parentspec,
                                         childspec,
                                         ignore_ws=False):
+        """Return a list of parent :class:`~models_cisco.IOSCfgLine` objects,
+        which matched the ``parentspec`` and whose children do not match 
+        all elements in ``childspec``.  Only the parent 
+        :class:`~models_cisco.IOSCfgLine` objects will be returned.
+
+        Args:
+            - parentspec (str): Text regular expression for the 
:class:`~models_cisco.IOSCfgLine` object to be matched; this must match the 
parent's line
+            - childspec (str): A list of text regular expressions to be 
matched among the children
+        Kwargs:
+            - ignore_ws (bool): boolean that controls whether whitespace is 
ignored
+
+        Returns:
+            - list.  A list of matching parent 
:class:`~models_cisco.IOSCfgLine` objects"""
         assert bool(getattr(childspec, 'append'))  # Childspec must be a list
         retval = list()
         if ignore_ws:
@@ -1574,10 +1602,7 @@
         """Find all :class:`~models_cisco.IOSCfgLine` objects whose text 
         matches linespec, and delete the object"""
         objs = self.find_objects(linespec, exactmatch, ignore_ws)
-        #atomic = False
-        for idx, obj in enumerate(objs):
-            #if idx==last_idx:
-            #    atomic = True
+        for obj in reversed(objs):
             del self.ConfigObjs[obj.linenum]
 
     def prepend_line(self, linespec):
@@ -1814,6 +1839,91 @@
 
         return retval
 
+    def re_search_children(self, regex, recurse=False):
+        """Use ``regex`` to search for root parents in the config with text 
matching regex.  If recurse is False, only root parent objects are returned.
+
+        Args:
+            - regex (str): A string or python regular expression, which should 
be matched.
+            - recurse (bool): Set True if you want to search all objects, and 
not just the root parents
+
+        Returns:
+            - list.  A list of matching :class:`~models_cisco.IOSCfgLine` 
objects which matched.  If there is no match, an empty :py:func:`list` is 
returned.
+
+        """
+        if recurse is False:
+            # Only return the matching oldest ancestor objects...
+            return [obj for obj in self.find_objects(regex) \
+                if (obj.parent is obj)]
+        else:
+            # Return any matching object
+            return [obj for obj in self.find_objects(regex)]
+
+    def re_match_iter_typed(self, regex, group=1, result_type=str, default='',
+        untyped_default=False):
+        r"""Use ``regex`` to search the root parents in the config
+        and return the contents of the regular expression group, at the 
+        integer ``group`` index, cast as ``result_type``; if there is no 
+        match, ``default`` is returned.
+
+        Args:
+            - regex (str): A string or python compiled regular expression, 
which should be matched.  This regular expression should contain parenthesis, 
which bound a match group.
+        Kwargs:
+            - group (int): An integer which specifies the desired regex group 
to be returned.  ``group`` defaults to 1.
+            - result_type (type): A type (typically one of: ``str``, ``int``, 
``float``, or :class:`~ccp_util.IPv4Obj`).         All returned values are cast 
as ``result_type``, which defaults to ``str``.
+            - default (any): The default value to be returned, if there is no 
match.
+            - untyped_default (bool): Set True if you don't want the default 
value to be typed
+
+        Returns:
+            - ``result_type``.  The text matched by the regular expression 
group; if there is no match, ``default`` is returned.  All values are cast as 
``result_type``.
+
+
+        This example illustrates how you can use
+        :func:`~ciscoconfparse.re_match_iter_typed` to get the
+        first interface name listed in the config.
+
+           >>> from ciscoconfparse import CiscoConfParse
+           >>> config = [
+           ...     '!',
+           ...     'interface Serial1/0',
+           ...     ' ip address 1.1.1.1 255.255.255.252',
+           ...     '!',
+           ...     'interface Serial2/0',
+           ...     ' ip address 1.1.1.5 255.255.255.252',
+           ...     '!',
+           ...     ]
+           >>> parse = CiscoConfParse(config)
+           >>> INTF_RE = re.compile(r'interface\s(\S+)')
+           >>> parse.re_match_iter_typed(INTF_RE)
+           Serial1/0
+           >>>
+
+        """
+        ## iterate through root objects, and return the matching value
+        ##  (cast as result_type) from the first object.text that matches regex
+
+        #if (default is True):
+            ## Not using self.re_match_iter_typed(default=True), because I want
+            ##   to be sure I build the correct API for match=False
+            ##
+            ## Ref IOSIntfLine.has_dtp for an example of how to code around
+            ##   this while I build the API
+        #    raise NotImplementedError
+
+        for cobj in self.ConfigObjs:
+
+            # Only process parent objects at the root of the tree...
+            if cobj.parent is not cobj:
+                continue
+
+            mm = re.search(regex, cobj.text)
+            if not (mm is None):
+                return result_type(mm.group(group))
+        ## Ref Github issue #121
+        if untyped_default:
+            return default
+        else:
+            return result_type(default)
+
     def req_cfgspec_all_diff(self, cfgspec, ignore_ws=False):
         """
         req_cfgspec_all_diff takes a list of required configuration lines,
@@ -2645,8 +2755,8 @@
 
     def config_hierarchy(self):
         """Walk this configuration and return the following tuple
-        at each parent 'level':
-            (list_of_parent_sibling_objs, list_of_nonparent_sibling_objs)
+        at each parent 'level': (list_of_parent_sibling_objs, 
list_of_nonparent_sibling_objs)
+
         """
         parent_siblings = list()
         nonparent_siblings = list()
@@ -2725,8 +2835,25 @@
                 except IndexError:
                     break
 
+    def _macro_mark_children(self, macro_parent_idx_list):
+        # Mark macro children appropriately...
+        for idx in macro_parent_idx_list:
+            pobj = self._list[idx]
+            pobj.child_indent = 0
+
+            # Walk the next configuration lines looking for the macro's 
children
+            finished = False
+            while not finished:
+                idx += 1
+                cobj = self._list[idx]
+                cobj.parent = pobj
+                pobj.children.append(cobj)
+                # If we hit the end of the macro, break out of the loop
+                if cobj.text.rstrip()=='@':
+                    finished = True
+
     def _bootstrap_obj_init(self, text_list):
-        """Accept a text list and format into proper objects"""
+        """Accept a text list and format into proper IOSCfgLine() objects"""
         # Append text lines as IOSCfgLine objects...
         BANNER_STR = set([
             'login',
@@ -2744,6 +2871,7 @@
         idx = 0
 
         max_indent = 0
+        macro_parent_idx_list = list()
         parents = dict()
         for line in text_list:
             # Reject empty lines if ignore_blank_lines...
@@ -2765,6 +2893,10 @@
 
             is_config_line = obj.is_config_line
 
+            # list out macro parent line numbers...
+            if obj.text[0:11]=='macro name ':
+                macro_parent_idx_list.append(obj.linenum)
+
             ## Parent cache:
             ## Maintain indent vs max_indent in a family and
             ##     cache the parent until indent<max_indent
@@ -2821,6 +2953,10 @@
 
         self._list = retval
         self._banner_mark_regex(BANNER_RE)
+        # We need to use a different method for macros than banners because
+        #   macros don't specify a delimiter on their parent line, but
+        #   banners call out a delimiter.
+        self._macro_mark_children(macro_parent_idx_list) # Process macros
         return retval
 
     def _add_child_to_parent(self, _list, idx, indent, parentobj, childobj):
@@ -3139,6 +3275,7 @@
                 except IndexError:
                     break
 
+
     def _bootstrap_obj_init(self, text_list):
         """Accept a text list and format into proper objects"""
         # Append text lines as NXOSCfgLine objects...
@@ -3177,6 +3314,7 @@
 
             is_config_line = obj.is_config_line
 
+
             ## Parent cache:
             ## Maintain indent vs max_indent in a family and
             ##     cache the parent until indent<max_indent
@@ -3232,7 +3370,7 @@
             idx += 1
 
         self._list = retval
-        self._banner_mark_regex(BANNER_RE)
+        self._banner_mark_regex(BANNER_RE) # Process IOS banners
         return retval
 
     def _add_child_to_parent(self, _list, idx, indent, parentobj, childobj):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/errors.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/errors.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/errors.py  2019-02-19 
03:02:10.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/errors.py   2019-05-12 
03:58:23.000000000 +0200
@@ -1,3 +1,5 @@
+from __future__  import absolute_import
+
 class BaseError(Exception):
     def __init__(self, msg=""):
         super(BaseError, self).__init__(msg)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/models_asa.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/models_asa.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/models_asa.py      2019-02-19 
03:02:10.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/models_asa.py       2019-05-12 
03:58:53.000000000 +0200
@@ -1,9 +1,10 @@
+from __future__  import absolute_import
 import re
 
-from protocol_values import ASA_TCP_PORTS, ASA_UDP_PORTS, ASA_IP_PROTOCOLS
-from ccp_abc import BaseCfgLine
-from ccp_util import L4Object
-from ccp_util import IPv4Obj
+from ciscoconfparse.protocol_values import ASA_TCP_PORTS, ASA_UDP_PORTS, 
ASA_IP_PROTOCOLS
+from ciscoconfparse.ccp_abc import BaseCfgLine
+from ciscoconfparse.ccp_util import L4Object
+from ciscoconfparse.ccp_util import IPv4Obj
 
 ### HUGE UGLY WARNING:
 ###   Anything in models_asa.py could change at any time, until I remove this
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/models_cisco.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/models_cisco.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/models_cisco.py    2019-03-17 
02:57:15.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/models_cisco.py     2019-06-27 
13:38:04.000000000 +0200
@@ -1,14 +1,15 @@
+from __future__  import absolute_import
 import traceback
 import sys
 import re
 import os
 
-from errors import DynamicAddressException
+from ciscoconfparse.errors import DynamicAddressException
 
-from ccp_util import _IPV6_REGEX_STR_COMPRESSED1, _IPV6_REGEX_STR_COMPRESSED2
-from ccp_util import _IPV6_REGEX_STR_COMPRESSED3
-from ccp_util import CiscoRange, IPv4Obj, IPv6Obj
-from ccp_abc import BaseCfgLine
+from ciscoconfparse.ccp_util import _IPV6_REGEX_STR_COMPRESSED1, 
_IPV6_REGEX_STR_COMPRESSED2
+from ciscoconfparse.ccp_util import _IPV6_REGEX_STR_COMPRESSED3
+from ciscoconfparse.ccp_util import CiscoRange, IPv4Obj, IPv6Obj
+from ciscoconfparse.ccp_abc import BaseCfgLine
 
 ### HUGE UGLY WARNING:
 ###   Anything in models_cisco.py could change at any time, until I remove this
@@ -741,10 +742,14 @@
             r'^\s*carrier-delay\s+msec\s+(\d+)$',
             result_type=float,
             default=0.0)
+
         if (cd_seconds > 0.0):
             return cd_seconds
-        else:
+        elif (cd_msec > 0.0):
             return cd_msec / 1000.0
+        else:
+            return 0.0
+
 
     @property
     def has_manual_clock_rate(self):
@@ -1362,22 +1367,26 @@
     def hsrp_hello_timer(self):
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
+
+        # FIXME: handle msec timers...
         retval = self.re_match_iter_typed(
             r'^\s*standby\s+(\d+\s+)*timers\s+(\d+)\s+\d+',
             group=2,
-            result_type=int,
-            default=0)
+            result_type=float,
+            default=0.0)
         return retval
 
     @property
     def hsrp_hold_timer(self):
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
+
+        # FIXME: this should be a float (in case of msec timers)
         retval = self.re_match_iter_typed(
             r'^\s*standby\s+(\d+\s+)*timers\s+\d+\s+(\d+)',
             group=2,
-            result_type=int,
-            default=0)
+            result_type=float,
+            default=0.0)
         return retval
 
     @property
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/models_junos.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/models_junos.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/models_junos.py    2019-02-19 
03:02:10.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/models_junos.py     2019-05-12 
03:59:09.000000000 +0200
@@ -1,9 +1,10 @@
+from __future__  import absolute_import
 import sys
 import re
 import os
 
-from ccp_abc import BaseCfgLine
-from ccp_util import IPv4Obj
+from ciscoconfparse.ccp_abc import BaseCfgLine
+from ciscoconfparse.ccp_util import IPv4Obj
 
 ### HUGE UGLY WARNING:
 ###   Anything in models_junos.py could change at any time, until I remove this
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/models_nxos.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/models_nxos.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/models_nxos.py     2019-03-17 
13:50:56.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/models_nxos.py      2019-07-27 
20:33:11.000000000 +0200
@@ -1,13 +1,14 @@
+from __future__  import absolute_import
 import sys
 import re
 import os
 
-from errors import DynamicAddressException
+from ciscoconfparse.errors import DynamicAddressException
 
-from ccp_util import _IPV6_REGEX_STR_COMPRESSED1, _IPV6_REGEX_STR_COMPRESSED2
-from ccp_util import _IPV6_REGEX_STR_COMPRESSED3
-from ccp_util import CiscoRange, IPv4Obj, IPv6Obj
-from ccp_abc import BaseCfgLine
+from ciscoconfparse.ccp_util import _IPV6_REGEX_STR_COMPRESSED1, 
_IPV6_REGEX_STR_COMPRESSED2
+from ciscoconfparse.ccp_util import _IPV6_REGEX_STR_COMPRESSED3
+from ciscoconfparse.ccp_util import CiscoRange, IPv4Obj, IPv6Obj
+from ciscoconfparse.ccp_abc import BaseCfgLine
 
 ### HUGE UGLY WARNING:
 ###   Anything in models_nxos.py could change at any time, until I remove this
@@ -748,8 +749,10 @@
             default=0.0)
         if (cd_seconds > 0.0):
             return cd_seconds
-        else:
+        elif (cd_msec > 0.0):
             return cd_msec / 1000.0
+        else:
+            return 0.0
 
     @property
     def has_manual_clock_rate(self):
@@ -1284,17 +1287,19 @@
         ## NOTE: I have no intention of checking self.is_shutdown here
         ##     People should be able to check the sanity of interfaces
         ##     before they put them into production
+        retval = ''
 
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
         if (self.ipv4_addr == ''):
             return ''
 
-        retval = self.re_match_iter_typed(
-            r'^\s*standby\s+(\d+\s+)*ip\s+(\S+)',
-            group=2,
-            result_type=str,
-            default='')
+        for hsrpobj in self.children:
+            if hsrpobj.re_match_typed(r'^\s+hsrp\s+(\d+)'):
+                for child in hsrpobj.children:
+                    retval = child.re_match_typed(r'^\s+ip\s+(\S+)')
+                    if retval: 
+                        return retval
         return retval
 
     @property
@@ -1302,6 +1307,7 @@
         ## NOTE: I have no intention of checking self.is_shutdown here
         ##     People should be able to check the sanity of interfaces
         ##     before they put them into production
+        retval = ''
 
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
@@ -1318,43 +1324,78 @@
     def hsrp_group(self):
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
-        retval = self.re_match_iter_typed(
-            r'^\s*standby\s+(\d+)\s+ip\s+\S+', result_type=int, default=-1)
+        retval = ''
+        for hsrpobj in self.children:
+            retval = hsrpobj.re_match_typed(r'^\s+hsrp\s+(\d+)')
+            if retval:
+                return retval
         return retval
 
     @property
     def hsrp_priority(self):
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
+        DEFAULT_PRI = 100
         if not self.has_ip_hsrp:
             return 0  # Return this if there is no hsrp on the interface
-        retval = self.re_match_iter_typed(
-            r'^\s*standby\s+(\d+\s+)*priority\s+(\d+)',
-            group=2,
-            result_type=int,
-            default=100)
-        return retval
+        for hsrpobj in self.children:
+            if hsrpobj.re_match_typed(r'^\s+hsrp\s+(\d+)'):
+                retval = hsrpobj.re_match_iter_typed(r'^\s+priority\s+(\d+)',
+                    result_type=int,
+                    default=DEFAULT_PRI)
+                if retval!=DEFAULT_PRI:
+                    return retval
 
     @property
     def hsrp_hello_timer(self):
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
-        retval = self.re_match_iter_typed(
-            r'^\s*standby\s+(\d+\s+)*timers\s+(\d+)\s+\d+',
-            group=2,
-            result_type=int,
-            default=0)
+
+        #timers msec 250 msec 750
+        for hsrpobj in self.children:
+            if hsrpobj.re_match_typed(r'^\s+hsrp\s+(\d+)'):
+                timer_sec = hsrpobj.re_match_iter_typed(
+                    r'^\s+timers\s+(\d+)\s+\d+',
+                    result_type=float,
+                    default=0.0
+                    )
+                timer_msec = hsrpobj.re_match_iter_typed(
+                    r'^\s+timers\s+msec\s+(\d+)\s+msec\s+\d+',
+                    result_type=float,
+                    default=0.0
+                    )
+                if (timer_sec > 0.0):
+                    return timer_sec
+                elif (timer_msec > 0.0):
+                    return timer_msec / 1000.0
+                return 0.0
+
         return retval
 
     @property
     def hsrp_hold_timer(self):
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
-        retval = self.re_match_iter_typed(
-            r'^\s*standby\s+(\d+\s+)*timers\s+\d+\s+(\d+)',
-            group=2,
-            result_type=int,
-            default=0)
+
+        #timers msec 250 msec 750
+        for hsrpobj in self.children:
+            if hsrpobj.re_match_typed(r'^\s+hsrp\s+(\d+)'):
+                timer_sec = hsrpobj.re_match_iter_typed(
+                    r'^\s+timers\s+\d+\s+(\d+)',
+                    result_type=float,
+                    default=0.0
+                    )
+                timer_msec = hsrpobj.re_match_iter_typed(
+                    r'^\s+timers\s+msec\s+\d+\s+msec\s+(\d+)',
+                    result_type=float,
+                    default=0.0
+                    )
+                if (timer_sec > 0.0):
+                    return timer_sec
+                elif (timer_msec > 0.0):
+                    return timer_msec / 1000.0
+                return 0.0
+
         return retval
 
     @property
@@ -1365,11 +1406,12 @@
     def hsrp_track(self):
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
-        retval = self.re_match_iter_typed(
-            r'^\s*standby\s+(\d+\s+)*track\s(\S+.+?)\s+\d+\s*',
-            group=2,
-            result_type=str,
-            default='')
+        retval = ''
+        for hsrpobj in self.children:
+            if hsrpobj.re_match_typed(r'^\s+hsrp\s+(\d+)'):
+                retval = hsrpobj.re_match_iter_typed(r'^\s+track\s+(\d+)',
+                    result_type=str,
+                    default='')
         return retval
 
     @property
@@ -1377,8 +1419,8 @@
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
         retval = self.re_match_iter_typed(
-            r'^\s*standby\s+(\d+\s+)*(use-bia)',
-            group=2,
+            r'^\s*hsrp\s+(use-bia)',
+            group=1,
             result_type=bool,
             default=False)
         return retval
@@ -1387,15 +1429,19 @@
     def has_hsrp_preempt(self):
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
-        retval = self.re_match_iter_typed(
-            r'^\s*standby\s+(\d+\s+)*(use-bia)',
-            group=2,
-            result_type=bool,
-            default=False)
+        retval = False
+        for hsrpobj in self.children:
+            if hsrpobj.re_match_typed(r'^\s+hsrp\s+(\d+)'):
+                retval = hsrpobj.re_match_iter_typed(
+                    r'^\s+(preempt)',
+                    group=1,
+                    result_type=bool,
+                    default=False)
         return retval
 
     @property
     def hsrp_authentication_md5_keychain(self):
+        ## FIXME nxos
         ## For API simplicity, I always assume there is only one hsrp 
         ##     group on the interface
         retval = self.re_match_iter_typed(
@@ -1407,6 +1453,7 @@
 
     @property
     def has_hsrp_authentication_md5(self):
+        ## FIXME nxos
         keychain = self.hsrp_authentication_md5_keychain
         return bool(keychain)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ciscoconfparse-1.3.32/ciscoconfparse/protocol_values.py 
new/ciscoconfparse-1.4.1/ciscoconfparse/protocol_values.py
--- old/ciscoconfparse-1.3.32/ciscoconfparse/protocol_values.py 2019-02-19 
03:02:10.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/protocol_values.py  2019-05-12 
03:59:26.000000000 +0200
@@ -1,3 +1,4 @@
+from __future__  import absolute_import
 """ protocol_values.py - Parse, Query, Build, and Modify IOS-style 
configurations
      Copyright (C) 2014-2015 David Michael Pennington
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/ciscoconfparse/version 
new/ciscoconfparse-1.4.1/ciscoconfparse/version
--- old/ciscoconfparse-1.3.32/ciscoconfparse/version    2019-03-17 
13:45:07.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse/version     2019-07-28 
15:18:04.000000000 +0200
@@ -1 +1 @@
-1.3.32
+1.4.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ciscoconfparse-1.3.32/ciscoconfparse.egg-info/PKG-INFO 
new/ciscoconfparse-1.4.1/ciscoconfparse.egg-info/PKG-INFO
--- old/ciscoconfparse-1.3.32/ciscoconfparse.egg-info/PKG-INFO  2019-03-17 
13:53:07.000000000 +0100
+++ new/ciscoconfparse-1.4.1/ciscoconfparse.egg-info/PKG-INFO   2019-07-28 
15:36:01.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: ciscoconfparse
-Version: 1.3.32
+Version: 1.4.1
 Summary: Parse, Audit, Query, Build, and Modify Cisco IOS-style configurations
 Home-page: http://www.pennington.net/py/ciscoconfparse/
 Author: David Michael Pennington
@@ -82,34 +82,33 @@
         Pre-requisites
         ==============
         
-        As of version 1.3.32, ciscoconfparse_ requires Python versions 2.7 or 
3.4+ 
-        (note: version 3.7.0 has a bug - ref Github issue #117, but version 
3.7.1 
-        works); the OS should not matter.
+        ciscoconfparse_ requires Python versions 2.7 or 3.4+ (note: version 
3.7.0 has 
+        a bug - ref Github issue #117, but version 3.7.1 works); the OS should 
not 
+        matter.
         
         .. _Installation:
         
         Installation and Downloads
         ==========================
         
-        The best way to get ciscoconfparse is with setuptools_ or pip_.  If 
you 
-        already have setuptools_, you can install as usual:
+        You can install into Python2.x with pip_:
         
         ::
         
-              # Substitute whatever ciscoconfparse version you like...
-              easy_install -U ciscoconfparse==1.3.32
+              pip install --upgrade ciscoconfparse
         
-        Alternatively you can install into Python2.x with pip_:
+        Use ``pip3`` for Python3.x...
         
         ::
         
-              pip install --upgrade ciscoconfparse
+              pip3 install --upgrade ciscoconfparse
         
-        Use ``pip3`` for Python3.x...
+        If you don't want to use pip_, you can install with `easy_install`:
         
         ::
         
-              pip3 install --upgrade ciscoconfparse
+              easy_install -U ciscoconfparse
+        
         
         Otherwise `download it from PyPi 
<https://pypi.python.org/pypi/ciscoconfparse>`_, extract it and run the 
``setup.py`` script:
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ciscoconfparse-1.3.32/setup.py 
new/ciscoconfparse-1.4.1/setup.py
--- old/ciscoconfparse-1.3.32/setup.py  2019-02-19 03:02:10.000000000 +0100
+++ new/ciscoconfparse-1.4.1/setup.py   2019-05-27 20:53:53.000000000 +0200
@@ -7,7 +7,7 @@
 import sys
 import os
 CURRENT_PATH=os.path.join(os.path.dirname(__file__))
-sys.path.insert(1,CURRENT_PATH)
+sys.path.insert(1, CURRENT_PATH)
 
 
 def read(fname):


Reply via email to