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 - pgajdos@suse.com
+
+- 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
interface Serial2/0
>>>
@@ -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[\{{\}}])*(?P<sc>\;)*\s*)
|(?P[\{{\}}\;])
+ |(?P\}}\;)\s*
|(?P^\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 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):