diff --git a/.github/labels/issue_labeler.yml b/.github/labels/issue_labeler.yml new file mode 100644 index 0000000..fe55a58 --- /dev/null +++ b/.github/labels/issue_labeler.yml @@ -0,0 +1,11 @@ +# Add/remove 'critical' label if issue contains the words 'urgent' or 'critical' +critical: + - '(critical|urgent)' + +# Add/remove 'bug' label if issue contains the words 'bug' +bug: + - '(bug)' + +# Add/remove 'feature' label if issue contains the words 'feature' +feature: + - '(feature)' \ No newline at end of file diff --git a/.github/labels/labeler.yml b/.github/labels/labeler.yml new file mode 100644 index 0000000..2806311 --- /dev/null +++ b/.github/labels/labeler.yml @@ -0,0 +1,30 @@ +--- +# configuration spec at https://github.com/actions/labeler/blob/master/README.md +sh/docs: + - CHANGELOG.md + - CONTRIBUTING.md + - LICENSE + - README.md +sh/tests: + - tests/* + - tox.ini +sh/code: + - data/* + - asserts/* + - shconfparser +sh/ci: + - .github/workflows/* + - .github/labels/* + - .travis.yml + - .coveralls.yml +sh/settings: + - setup.py + - tox.ini + - .gitignore + - .python-version +sh/pip: + - requirements.txt + - requirements_dev.txt + - requirements_dev27.txt + - Pipfile + - Pipfile.lock \ No newline at end of file diff --git a/.github/labels/labels.yml b/.github/labels/labels.yml new file mode 100644 index 0000000..11e1a92 --- /dev/null +++ b/.github/labels/labels.yml @@ -0,0 +1,49 @@ +--- +# Default GitHub labels +- color: d73a4a + name: bug + description: Something isn't working +- color: cfd3d7 + name: duplicate + description: This issue or pull request already exists +- color: a2eeef + name: enhancement + description: New feature or request +- color: 7057ff + name: good first issue + description: Good for newcomers +- color: 008672 + name: help wanted + description: Extra attention is needed +- color: e4e669 + name: invalid + description: This doesn't seem right +- color: d876e3 + name: question + description: Further information is requested +- color: ffffff + name: wontfix + description: This will not be worked on + +# Labels specific to shconfparser +- color: 0366d6 + name: sh/wip + description: Work in progress +- color: 0366d6 + name: sh/docs + description: Improvements or additions to documentation +- color: 0366d6 + name: sh/tests + description: Everything related to molecule tests and linters +- color: 0366d6 + name: sh/code + description: Bots, bots everywhere +- color: 0366d6 + name: sh/ci + description: Ansible variables used in role +- color: 0366d6 + name: sh/settings + description: Logic behind ansible role +- color: 0366d6 + name: sh/pip + description: Templates \ No newline at end of file diff --git a/.github/workflows/issue_labeler.yml b/.github/workflows/issue_labeler.yml new file mode 100644 index 0000000..09fa52a --- /dev/null +++ b/.github/workflows/issue_labeler.yml @@ -0,0 +1,15 @@ +name: "Issue Labeler" +on: + issues: + types: [opened, edited] + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: github/issue-labeler@v2.0 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + configuration-path: .github/labels/issue_labeler.yml + # not-before: 2020-01-15T02:54:32Z + enable-versioned-regex: 0 \ No newline at end of file diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..17ac2cf --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,13 @@ +--- +name: Pull request labeler +on: + schedule: + - cron: '*/15 * * * *' +jobs: + labeler: + runs-on: ubuntu-latest + steps: + - uses: paulfantom/periodic-labeler@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} \ No newline at end of file diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml new file mode 100644 index 0000000..3425cf5 --- /dev/null +++ b/.github/workflows/labels.yml @@ -0,0 +1,17 @@ +--- +name: Sync labels in the declarative way +on: + push: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@1.0.0 + - uses: micnncim/action-label-syncer@v0.3.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + with: + manifest: .github/labels/labels.yml \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..6b1df62 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,31 @@ +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build python package + run: | + python setup.py sdist bdist_wheel + - name: Publish package + uses: pypa/gh-action-pypi-publish@v1.4.2 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + skip_existing: true + verbose: true \ No newline at end of file diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 0000000..216dfbc --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,43 @@ +name: "pytest" + +on: + push: + branches: + - '**' + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: [3.7, 3.9] + os: [ubuntu-latest] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements_dev.txt + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest -v --cov=shconfparser + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + name: ${{ matrix.os }} Python ${{ matrix.python-version }} + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true \ No newline at end of file diff --git a/.github/workflows/pytest_27.yml b/.github/workflows/pytest_27.yml new file mode 100644 index 0000000..ba84e79 --- /dev/null +++ b/.github/workflows/pytest_27.yml @@ -0,0 +1,31 @@ +name: "pytest 2.7.x" + +on: + push: + branches: + - '**' + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: [2.7] + os: [ubuntu-latest] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements_dev27.txt + - name: Test with pytest + run: | + pytest -v --cov=shconfparser diff --git a/.github/workflows/remove_labeler.yml b/.github/workflows/remove_labeler.yml new file mode 100644 index 0000000..295f7fb --- /dev/null +++ b/.github/workflows/remove_labeler.yml @@ -0,0 +1,14 @@ +name: Remove All Labels + +on: + pull_request: + types: [closed] + +jobs: + remove_labels: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: rogerluan/label-remover@v1.0.1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 22ec05a..8454c6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ +# Editor specific + +.idea +.idea/ +.vscode/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e69de29 diff --git a/requirements_dev.txt b/requirements_dev.txt index 31f30fa..3f93aa1 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -2,4 +2,6 @@ pytest-cov==2.8.1 pytest==5.3.0 coveralls==1.5.1 tox==3.7.0 +wheel +flake8 diff --git a/shconfparser/search.py b/shconfparser/search.py index 14cc61c..5509336 100644 --- a/shconfparser/search.py +++ b/shconfparser/search.py @@ -16,36 +16,37 @@ def validate(self, data, dtype=OrderedDict): """ if data == None: return None - if type(data) != dtype: return None - return True def get_pattern(self, pattern, strip=True): """ This method converts the given string to regex pattern """ - if type(pattern) == re.Pattern: - return pattern + try: + if type(pattern) == re.Pattern: + return pattern + except AttributeError: + if type(pattern) != str: + return pattern if strip and type(pattern) == str: pattern = pattern.strip() return re.compile(pattern) - def search_in_tree(self, pattern, data=None): if not self.validate(data): return None p = self.get_pattern(pattern) for key in data.keys(): - if p.match(key): - return key + res = p.match(key) + if res: + return res return None - def search_all_in_tree(self, pattern, data=None): if not self.validate(data): return None @@ -53,9 +54,9 @@ def search_all_in_tree(self, pattern, data=None): p = self.get_pattern(pattern) match = OrderedDict() for key in data.keys(): - if p.match(key): - match[key] = key - + res = p.match(key) + if res: + match[res] = key return match if len(match) else None def search_in_tree_level(self, pattern, data=None, level=0): @@ -75,7 +76,7 @@ def search_in_tree_level(self, pattern, data=None, level=0): return None def search_in_table(self, pattern, data=None, header_column=None): - if not self.validate(data): + if not self.validate(data, dtype=list): return None p = self.get_pattern(pattern) @@ -84,13 +85,13 @@ def search_in_table(self, pattern, data=None, header_column=None): return each_row def search_all_in_table(self, pattern, data=None, header_column=None): - if not self.validate(data): + if not self.validate(data, dtype=list): return None p = self.get_pattern(pattern) match = [] + match = [] for each_row in data: if p.match(each_row[header_column]): match.append(each_row) - return match if len(match) else None diff --git a/tests/test_search.py b/tests/test_search.py index 66f859d..07fdf5a 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -23,21 +23,27 @@ def setup(self): p.s.shcmd_dict['ip_interface_brief'] = p.parse_table(p.s.shcmd_dict['ip_interface_brief'], header) yield p + def test_search_in_tree_level(self, setup): + data = setup.s.shcmd_dict + pattern = r' privilege level 15' + m = setup.search.search_in_tree_level(pattern, data['running'], level=10) + assert pattern.strip() in m + def test_search_all_in_tree(self, setup): data = setup.s.shcmd_dict - pattern = 'interface\s+FastEthernet.*' + pattern = r'interface\s+FastEthernet.*' m = setup.search.search_all_in_tree(pattern, data['running']) assert 'interface FastEthernet0/0' in m.values() def test_search_in_tree(self, setup): data = setup.s.shcmd_dict - pattern = 'Cisco\s+IOS\s+Software.*' + pattern = r'Cisco\s+IOS\s+Software.*' m = setup.search.search_in_tree(pattern, data['version']) assert 'Version 12.4(25d)' in m.group(0) def test_search_in_table(self, setup): data = setup.s.shcmd_dict - pattern = 'R\d+' + pattern = r'R\d+' header = 'Device ID' m = setup.search.search_in_table(pattern, data['cdp_neighbors'], header) assert 'Device ID' in m @@ -45,7 +51,7 @@ def test_search_in_table(self, setup): def test_search_all_in_table(self, setup): data = setup.s.shcmd_dict - pattern = 'FastEthernet.*' + pattern = r'FastEthernet.*' header = 'Interface' m = setup.search.search_all_in_table(pattern, data['ip_interface_brief'], header) assert type(m) is list