Repository: warpnet/salt-lint
Pull request #293 (2023-01-06 15:24:57)

fix: octal rule triggers on time format
Time values such as this:

```
some_calendar_entry:
  file.managed:
    - name: /tmp/my_unit_file
    - contents: |
        OnCalendar=Sun 18:00
```

should not trigger this rule.

Changed the regex to accommodate this case while still catching/ignoring previous identified test cases


Patch:
diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml
index 500c17e..8785658 100644
--- a/.github/workflows/tox.yml
+++ b/.github/workflows/tox.yml
@@ -13,7 +13,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python: ['3.6', '3.7', '3.8', '3.9', '3.10']
+        python: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']
 
     steps:
 
@@ -29,6 +29,9 @@ jobs:
 
       - name: Run syntax checker using Tox
         run: tox -e lint
+        # Skip linting on EOL Python version because the pre-commit actions
+        # required supported versions.
+        if: ${{ matrix.python != '3.6' }}
 
       - name: Run Tox
         # Run tox using the version of Python in `PATH`
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a567bbc..cb0734e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -31,7 +31,7 @@ repos:
     - '--skip="./.git*,./saltlint/rules/FileManagedReplaceContentRule.py"'
     - '-L alse'
 - repo: https://github.com/PyCQA/pylint.git
-  rev: v2.9.5
+  rev: v2.15.9
   hooks:
   - id: pylint
     exclude: ^docs/
diff --git a/.pylintrc b/.pylintrc
index 294dfd0..f301cee 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -4,7 +4,6 @@ disable=
     missing-docstring,
     invalid-name,
     unused-argument,
-    no-self-use,
     useless-object-inheritance,
     too-many-instance-attributes,
     too-few-public-methods,
@@ -17,3 +16,4 @@ disable=
     dangerous-default-value,
     import-error,
     fixme,
+    consider-using-f-string,
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c317dff..3cf2118 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 ## [Unreleased]
 ### Added
 - Rule 219 for catching missing over-indentation of nested dicts ([#284](https://github.com/warpnet/salt-lint/pull/284)).
+- Add Python 3.11 support.
 
 ## [0.8.0] (2021-11-09)
 ### Fixed
diff --git a/docs/index.md b/docs/index.md
index e151876..a0ffc07 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -164,16 +164,13 @@ severity: True
 
 ### Pre-commit Setup
 
-To use salt-lint with [pre-commit](https://pre-commit.com),  just add the following to your local repo's `.pre-commit-config.yaml` file. Prior to version 0.12.0 of [pre-commit](https://pre-commit.com) the file was `hooks.yaml` (now `.pre-commit-config.yaml`).
+To use salt-lint with [pre-commit](https://pre-commit.com), just add the following to your local repo's `.pre-commit-config.yaml` file. Prior to version 0.12.0 of [pre-commit](https://pre-commit.com) the file was `hooks.yaml` (now `.pre-commit-config.yaml`).
 
 ```yaml
 ---
-
-# For use with pre-commit.
-# See usage instructions at http://pre-commit.com
-
--   repo: https://github.com/warpnet/salt-lint
-    rev: v0.7.0
+repos:
+  - repo: https://github.com/warpnet/salt-lint
+    rev: v0.8.0
     hooks:
       - id: salt-lint
 ```
diff --git a/saltlint/config.py b/saltlint/config.py
index be165f8..0b72acf 100644
--- a/saltlint/config.py
+++ b/saltlint/config.py
@@ -26,7 +26,7 @@ class Configuration(object):
 
         # Read the file contents
         if os.path.exists(file):
-            with open(file, 'r') as f:
+            with open(file, 'r', encoding="UTF-8") as f:
                 content = f.read()
         else:
             content = None
diff --git a/saltlint/formatters/default.py b/saltlint/formatters/default.py
index 4b5ad50..9909c7a 100644
--- a/saltlint/formatters/default.py
+++ b/saltlint/formatters/default.py
@@ -10,17 +10,17 @@ from saltlint.formatters.base import BaseFormatter
 class Formatter(BaseFormatter):
 
     def format(self, problem):
-        formatstr = u"{0} {1}\n{2}:{3}\n{4}\n"
+        formatstr = "{0} {1}\n{2}:{3}\n{4}\n"
 
         color = self.get_colors(self.colored)
         return formatstr.format(
-            u'{0}[{1}]{2}'.format(color['RED'], problem.rule.id,
-                                  color['ENDC']),
-            u'{0}{1}{2}'.format(color['LIGHT_RED'], problem.message,
-                                color['ENDC']),
-            u'{0}{1}{2}'.format(color['BLUE'], problem.filename,
-                                color['ENDC']),
-            u'{0}{1}{2}'.format(color['CYAN'], str(problem.linenumber),
-                                color['ENDC']),
-            u'{0}{1}{2}'.format(color['MAGENTA'], problem.line, color['ENDC'])
+            '{0}[{1}]{2}'.format(color['RED'], problem.rule.id,
+                                 color['ENDC']),
+            '{0}{1}{2}'.format(color['LIGHT_RED'], problem.message,
+                               color['ENDC']),
+            '{0}{1}{2}'.format(color['BLUE'], problem.filename,
+                               color['ENDC']),
+            '{0}{1}{2}'.format(color['CYAN'], str(problem.linenumber),
+                               color['ENDC']),
+            '{0}{1}{2}'.format(color['MAGENTA'], problem.line, color['ENDC'])
         )
diff --git a/saltlint/formatters/severity.py b/saltlint/formatters/severity.py
index 904521a..e879cb6 100644
--- a/saltlint/formatters/severity.py
+++ b/saltlint/formatters/severity.py
@@ -9,19 +9,19 @@ from saltlint.formatters.base import BaseFormatter
 class SeverityFormatter(BaseFormatter):
 
     def format(self, problem):
-        formatstr = u"{0} {sev} {1}\n{2}:{3}\n{4}\n"
+        formatstr = "{0} {sev} {1}\n{2}:{3}\n{4}\n"
 
         color = self.get_colors(self.colored)
         return formatstr.format(
-            u'{0}[{1}]{2}'.format(color['RED'], problem.rule.id,
-                                  color['ENDC']),
-            u'{0}{1}{2}'.format(color['LIGHT_RED'], problem.message,
-                                color['ENDC']),
-            u'{0}{1}{2}'.format(color['BLUE'], problem.filename,
-                                color['ENDC']),
-            u'{0}{1}{2}'.format(color['CYAN'], str(problem.linenumber),
-                                color['ENDC']),
-            u'{0}{1}{2}'.format(color['MAGENTA'], problem.line, color['ENDC']),
-            sev=u'{0}[{1}]{2}'.format(color['RED'], problem.rule.severity,
-                                      color['ENDC'])
+            '{0}[{1}]{2}'.format(color['RED'], problem.rule.id,
+                                 color['ENDC']),
+            '{0}{1}{2}'.format(color['LIGHT_RED'], problem.message,
+                               color['ENDC']),
+            '{0}{1}{2}'.format(color['BLUE'], problem.filename,
+                               color['ENDC']),
+            '{0}{1}{2}'.format(color['CYAN'], str(problem.linenumber),
+                               color['ENDC']),
+            '{0}{1}{2}'.format(color['MAGENTA'], problem.line, color['ENDC']),
+            sev='{0}[{1}]{2}'.format(color['RED'], problem.rule.severity,
+                                     color['ENDC'])
         )
diff --git a/saltlint/linter/match.py b/saltlint/linter/match.py
index f3aa709..aad456a 100644
--- a/saltlint/linter/match.py
+++ b/saltlint/linter/match.py
@@ -13,6 +13,6 @@ class Match(object):
         self.message = message or rule.shortdesc
 
     def __repr__(self):
-        formatstr = u"[{0}] ({1}) matched {2}:{3} {4}"
+        formatstr = "[{0}] ({1}) matched {2}:{3} {4}"
         return formatstr.format(self.rule.id, self.message,
                                 self.filename, self.linenumber, self.line)
diff --git a/saltlint/rules/NoIrregularSpacesRule.py b/saltlint/rules/NoIrregularSpacesRule.py
index 53a371f..9f12392 100644
--- a/saltlint/rules/NoIrregularSpacesRule.py
+++ b/saltlint/rules/NoIrregularSpacesRule.py
@@ -13,30 +13,30 @@ class NoIrregularSpacesRule(Rule):
     version_added = 'v0.1.0'
 
     irregular_spaces = [
-        u"\u000B",  # Line Tabulation (\v) - <VT>
-        u"\u000C",  # Form Feed (\f) - <FF>
-        u"\u00A0",  # No-Break Space - <NBSP>
-        u"\u0085",  # Next Line
-        u"\u1680",  # Ogham Space Mark
-        u"\u180E",  # Mongolian Vowel Separator - <MVS>
-        u"\uFEFF",  # Zero Width No-Break Space - <BOM>
-        u"\u2000",  # En Quad
-        u"\u2001",  # Em Quad
-        u"\u2002",  # En Space - <ENSP>
-        u"\u2003",  # Em Space - <EMSP>
-        u"\u2004",  # Tree-Per-Em
-        u"\u2005",  # Four-Per-Em
-        u"\u2006",  # Six-Per-Em
-        u"\u2007",  # Figure Space
-        u"\u2008",  # Punctuation Space - <PUNCSP>
-        u"\u2009",  # Thin Space
-        u"\u200A",  # Hair Space
-        u"\u200B",  # Zero Width Space - <ZWSP>
-        u"\u2028",  # Line Separator
-        u"\u2029",  # Paragraph Separator
-        u"\u202F",  # Narrow No-Break Space
-        u"\u205F",  # Medium Mathematical Space
-        u"\u3000",  # Ideographic Space
+        "\u000B",  # Line Tabulation (\v) - <VT>
+        "\u000C",  # Form Feed (\f) - <FF>
+        "\u00A0",  # No-Break Space - <NBSP>
+        "\u0085",  # Next Line
+        "\u1680",  # Ogham Space Mark
+        "\u180E",  # Mongolian Vowel Separator - <MVS>
+        "\uFEFF",  # Zero Width No-Break Space - <BOM>
+        "\u2000",  # En Quad
+        "\u2001",  # Em Quad
+        "\u2002",  # En Space - <ENSP>
+        "\u2003",  # Em Space - <EMSP>
+        "\u2004",  # Tree-Per-Em
+        "\u2005",  # Four-Per-Em
+        "\u2006",  # Six-Per-Em
+        "\u2007",  # Figure Space
+        "\u2008",  # Punctuation Space - <PUNCSP>
+        "\u2009",  # Thin Space
+        "\u200A",  # Hair Space
+        "\u200B",  # Zero Width Space - <ZWSP>
+        "\u2028",  # Line Separator
+        "\u2029",  # Paragraph Separator
+        "\u202F",  # Narrow No-Break Space
+        "\u205F",  # Medium Mathematical Space
+        "\u3000",  # Ideographic Space
         ]
 
     def match(self, file, line):
diff --git a/saltlint/rules/YamlHasOctalValueRule.py b/saltlint/rules/YamlHasOctalValueRule.py
index 2001e49..022d7d5 100644
--- a/saltlint/rules/YamlHasOctalValueRule.py
+++ b/saltlint/rules/YamlHasOctalValueRule.py
@@ -17,7 +17,7 @@ class YamlHasOctalValueRule(Rule):
     tags = ['formatting']
     version_added = 'v0.0.6'
 
-    bracket_regex = re.compile(r"^[^:]+:\s{0,}0[0-9]{1,}\s{0,}((?={#)|(?=#)|(?=$))")
+    bracket_regex = re.compile(r"^.*(?:\s|[^\d]{2}:)0\d+(?:[\s#{]|$)")
 
     def match(self, file, line):
         return self.bracket_regex.search(line)
diff --git a/setup.py b/setup.py
index b923209..5c86523 100644
--- a/setup.py
+++ b/setup.py
@@ -77,6 +77,7 @@ setup(
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
         'Topic :: Software Development :: Bug Tracking',
         'Topic :: Software Development :: Quality Assurance',
         'Topic :: Software Development :: Testing',
diff --git a/tox.ini b/tox.ini
index f769ce6..c3c99e1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,7 +4,7 @@
 # install tox" and then run "tox" from this directory.
 
 [tox]
-envlist = lint,{py36,py37,py38,py39,py310}-install,mypy
+envlist = lint,{py36,py37,py38,py39,py310,py311}-install,mypy
 skip_missing_interpreters = True
 
 [testenv]
@@ -47,6 +47,10 @@ commands = {[testenv:install]commands}
 skip_install = {[testenv:install]skip_install}
 commands = {[testenv:install]commands}
 
+[testenv:py311-install]
+skip_install = {[testenv:install]skip_install}
+commands = {[testenv:install]commands}
+
 [testenv:lint]
 description = Run all linters
 basepython = python3


Test patch:
diff --git a/tests/unit/TestYamlHasOctalValueRule.py b/tests/unit/TestYamlHasOctalValueRule.py
index e03fc44..e4e52ad 100644
--- a/tests/unit/TestYamlHasOctalValueRule.py
+++ b/tests/unit/TestYamlHasOctalValueRule.py
@@ -31,9 +31,20 @@ apache_disable_default_site:
 
 # MAC addresses shouldn't be matched, for more information see:
 # https://github.com/warpnet/salt-lint/issues/202
-infoblox_remove_record:
+infoblox_remove_record1:
   infoblox_host_record.absent:
     - mac: 4c:f2:d3:1b:2e:05
+
+infoblox_remove_record2:
+  infoblox_host_record.absent:
+    - mac: 05:f2:d3:1b:2e:4c
+
+# Time values should not trigger this rule
+some_calendar_entry:
+  file.managed:
+    - name: /tmp/my_unit_file
+    - contents: |
+        OnCalendar=Sun 18:00
 '''
 
 BAD_NUMBER_STATE = '''