Skip to content

Commit

Permalink
Version 0.7.9
Browse files Browse the repository at this point in the history
  • Loading branch information
mpenning committed Mar 6, 2024
1 parent 1403c89 commit e646033
Show file tree
Hide file tree
Showing 7 changed files with 617 additions and 421 deletions.
13 changes: 13 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
- Summary:
- Insert something here

## Version: 0.7.9

- Released: 2024-03-05
- Summary:
- Add `--unique` flag to `ccp ipgrep`
- Add `--line` flag to `ccp ipgrep`
- Add `--word_delimiter` argument to `ccp ipgrep`
- Prevent `--subnet` from being used more than once with `ccp ipgrep`
- Add tests for `ccp` utility
- Bump `dnspython` and `tomlkit` dependency versions to the latest
- Misc code refactoring
- More documentation updates

## Version: 0.7.8

- Released: 2024-03-04
Expand Down
126 changes: 80 additions & 46 deletions ciscoconfparse2/ccp_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1702,69 +1702,103 @@ def re_list_iter_typed(
logger.info(f"{self}.re_list_iter_typed(`regex`={regex}, `group`={group}, `result_type`={result_type}, `recurse`={recurse}, `groupdict`={groupdict}, `debug`={debug}) was called")

retval = list()

if groupdict is None:
if debug is True:
logger.debug(f" {self}.re_list_iter_typed() is checking with `groupdict`=None")
return self.re_list_iter_typed_groupdict_none(regex = regex,
group = group,
result_type = result_type,
groupdict = groupdict,
recurse = recurse,
debug = debug)
elif isinstance(groupdict, dict) is True:
return self.re_list_iter_typed_groupdict_dict(regex = regex,
group = group,
result_type = result_type,
groupdict = groupdict,
recurse = recurse,
debug = debug)
else:
error = f"`groupdict` must be None or a `dict`, but we got {type(groupdict)}."
logger.critical(error)
raise ValueError(error)

# Append to return values if the parent line matches the regex...
mm = re.search(regex, self.text)
if isinstance(mm, re.Match):
retval.append(result_type(mm.group(group)))
@logger.catch(reraise=True)
def re_list_iter_typed_groupdict_none(self,
regex: Union[str, re.Pattern],
group: int=1,
result_type: type=str,
groupdict: dict=None,
recurse: bool=True,
debug: bool=False,):
if debug is True:
logger.debug(f" {self}.re_list_iter_typed_groupdict_none() is checking with `groupdict`=None")

if recurse is False:
for cobj in self.children:
if debug is True:
logger.debug(f" {self}.re_list_iter_typed() is checking match of r'''{regex}''' on -->{cobj}<--")
mm = re.search(regex, cobj.text)
if isinstance(mm, re.Match):
retval.append(result_type(mm.group(group)))
else:
for cobj in self.all_children:
if debug is True:
logger.debug(f" {self}.re_list_iter_typed() is checking match of r'''{regex}''' on -->{cobj}<--")
mm = re.search(regex, cobj.text)
if isinstance(mm, re.Match):
retval.append(result_type(mm.group(group)))
return retval
elif isinstance(groupdict, dict) is True:
if debug is True:
logger.debug(f" {self}.re_list_iter_typed() is checking with `groupdict`={groupdict}")
retval = list()

# Return the result if the parent line matches the regex...
mm = re.search(regex, self.text)
if isinstance(mm, re.Match):
# Append to return values if the parent line matches the regex...
mm = re.search(regex, self.text)
if isinstance(mm, re.Match):
retval.append(result_type(mm.group(group)))

if recurse is False:
for cobj in self.children:
if debug is True:
logger.debug(f" {self}.re_list_iter_typed() is checking match of r'''{regex}''' on -->{cobj}<--")
mm = re.search(regex, cobj.text)
if isinstance(mm, re.Match):
retval.append(result_type(mm.group(group)))
else:
for cobj in self.all_children:
if debug is True:
logger.debug(f" {self}.re_list_iter_typed() is checking match of r'''{regex}''' on -->{cobj}<--")
mm = re.search(regex, cobj.text)
if isinstance(mm, re.Match):
retval.append(result_type(mm.group(group)))
return retval

@logger.catch(reraise=True)
def re_list_iter_typed_groupdict_dict(self,
regex: Union[str, re.Pattern],
group: int=1,
result_type: type=str,
groupdict: dict=None,
recurse: bool=True,
debug: bool=False,):
if debug is True:
logger.debug(f" {self}.re_list_iter_typed() is checking with `groupdict`={groupdict}")

# Return the result if the parent line matches the regex...
mm = re.search(regex, self.text)
if isinstance(mm, re.Match):
tmp = self.get_regex_typed_dict(
regex=mm,
type_dict=groupdict,
debug=debug,
)
retval.append(tmp)

if recurse is False:
for cobj in self.children:
mm = re.search(regex, cobj.text)
tmp = self.get_regex_typed_dict(
regex=mm,
type_dict=groupdict,
debug=debug,
)
retval.append(tmp)
return retval

if recurse is False:
for cobj in self.children:
mm = re.search(regex, cobj.text)
else:
for cobj in self.all_children:
mm = re.search(regex, cobj.text)
if isinstance(mm, re.Match):
tmp = self.get_regex_typed_dict(
regex=mm,
type_dict=groupdict,
debug=debug,
)
retval.append(tmp)
return retval
else:
for cobj in self.all_children:
mm = re.search(regex, cobj.text)
if isinstance(mm, re.Match):
tmp = self.get_regex_typed_dict(
regex=mm,
type_dict=groupdict,
debug=debug,
)
retval.append(tmp)
return retval
else:
error = f"`groupdict` must be None or a `dict`, but we got {type(groupdict)}."
logger.critical(error)
raise ValueError(error)
return retval

# On BaseCfgLine()
@property
Expand Down
14 changes: 7 additions & 7 deletions ciscoconfparse2/ciscoconfparse2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2619,16 +2619,16 @@ def _find_child_object_branches(
# List[List[BaseCfgLine]]
def find_object_branches(self,
branchspec: Union[tuple[str, ...], List[str]] = (),
regex_flags: Union[re.RegexFlag,int] = 0,
regex_flags: Union[re.RegexFlag, int] = 0,
regex_groups: bool = False,
empty_branches: bool = False,
reverse: bool = False,
debug: int = 0,) -> List[Any]:
r"""Iterate over a tuple of regular expression strings in `branchspec` and return matching objects in a list of lists (consider it similar to a table of matching config objects). `branchspec` expects to start at some ancestor and walk through the nested object hierarchy (with no limit on depth).
r"""A branch is just a list of all matching parent and child text lines. This method iterates over a tuple of regular expression strings in `branchspec` and returns matching objects in a list of lists (consider it similar to a table of matching config objects). `branchspec` expects to start at a parent line and walk through the nested child lines below it (with no limit on depth).
Previous CiscoConfParse() methods only handled a single parent regex and single child regex (such as :func:`~ciscoconfparse2.CiscoConfParse.find_objects`).
Transcend past one-level of parent-child relationship parsing to include multiple nested 'branches' of a single family (i.e. parents, children, grand-children, great-grand-children, etc). The result of handling longer regex chains is that it flattens what would otherwise be nested loops in your scripts; this makes parsing heavily-nested configuratations like Juniper, Palo-Alto, and F5 much simpler. Of course, there are plenty of applications for "flatter" config formats like IOS.
Use this method to transcend past one-level of parent-child relationship parsing to include nested 'branches' statements in a single family (i.e. parents, children, grand-children, great-grand-children, etc). The result of handling longer regex chains is that it flattens what would otherwise be nested loops in your scripts; this makes parsing heavily-nested configuratations like Juniper, Palo-Alto, and F5 much simpler. Of course, there are plenty of applications for normally "flatter" config formats like IOS.
Return a list of lists (of object 'branches') which are nested to the same depth required in `branchspec`. However, unlike most other CiscoConfParse() methods, return an explicit `None` if there is no object match. Returning `None` allows a single search over configs that may not be uniformly nested in every branch.
Expand Down Expand Up @@ -3265,7 +3265,7 @@ def find_child_objects(
Do not set ``childspec`` if searching with a tuple of strings or list of strings.
This example finds the object for "ge-0/0/0" under "interfaces" in the
This example finds the object for "ge-0/0/1" under "interfaces" in the
following config...
.. parsed-literal::
Expand Down Expand Up @@ -3296,9 +3296,9 @@ def find_child_objects(
<IOSCfgLine # 7 ' ge-0/0/1' (parent is # 0)>
We do this by quering `find_child_objects()`; we set our
parent as `^\s*interface` and set the child as
`^\s+ge-0/0/1`.
We do this by quering ``find_child_objects()``; we set our
parent as ``^\s*interface`` and set the child as
``^\s+ge-0/0/1``.
.. code-block:: python
:emphasize-lines: 22,23
Expand Down
Loading

0 comments on commit e646033

Please sign in to comment.