trigger 2.0.0__tar.gz → 2.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. {trigger-2.0.0/trigger.egg-info → trigger-2.0.2}/PKG-INFO +1 -1
  2. trigger-2.0.2/pyproject.toml +236 -0
  3. {trigger-2.0.0 → trigger-2.0.2}/tests/test_acl.py +15 -22
  4. {trigger-2.0.0 → trigger-2.0.2}/tests/test_acl_queue.py +6 -2
  5. {trigger-2.0.0 → trigger-2.0.2}/tests/test_except.py +2 -2
  6. {trigger-2.0.0 → trigger-2.0.2}/tests/test_netdevices.py +6 -8
  7. {trigger-2.0.0 → trigger-2.0.2}/tests/test_scripts.py +3 -2
  8. {trigger-2.0.0 → trigger-2.0.2}/tests/test_tacacsrc.py +19 -14
  9. {trigger-2.0.0 → trigger-2.0.2}/tests/test_templates.py +6 -2
  10. {trigger-2.0.0 → trigger-2.0.2}/tests/test_twister2.py +0 -4
  11. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/__init__.py +2 -3
  12. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/autoacl.py +3 -5
  13. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/db.py +35 -47
  14. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/dicts.py +2 -3
  15. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/grammar.py +3 -5
  16. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/ios.py +11 -12
  17. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/junos.py +26 -38
  18. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/models.py +16 -16
  19. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/parser.py +28 -34
  20. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/queue.py +48 -47
  21. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/support.py +147 -161
  22. {trigger-2.0.0 → trigger-2.0.2}/trigger/acl/tools.py +107 -122
  23. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/acl.py +8 -6
  24. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/acl_script.py +48 -46
  25. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/aclconv.py +4 -5
  26. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/check_access.py +10 -13
  27. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/check_syntax.py +10 -10
  28. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/fe.py +25 -23
  29. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/find_access.py +15 -20
  30. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/gnng.py +24 -35
  31. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/gong.py +5 -3
  32. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/load_acl.py +72 -67
  33. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/load_config.py +1 -2
  34. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/netdev.py +18 -23
  35. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/optimizer.py +29 -41
  36. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/run_cmds.py +1 -2
  37. {trigger-2.0.0 → trigger-2.0.2}/trigger/changemgmt/__init__.py +17 -29
  38. {trigger-2.0.0 → trigger-2.0.2}/trigger/changemgmt/bounce.py +3 -5
  39. {trigger-2.0.0 → trigger-2.0.2}/trigger/cmds.py +110 -126
  40. {trigger-2.0.0 → trigger-2.0.2}/trigger/conf/__init__.py +6 -7
  41. {trigger-2.0.0 → trigger-2.0.2}/trigger/conf/global_settings.py +35 -37
  42. {trigger-2.0.0 → trigger-2.0.2}/trigger/contrib/__init__.py +1 -2
  43. {trigger-2.0.0 → trigger-2.0.2}/trigger/exceptions.py +11 -21
  44. {trigger-2.0.0 → trigger-2.0.2}/trigger/gorc.py +12 -17
  45. {trigger-2.0.0 → trigger-2.0.2}/trigger/netdevices/__init__.py +84 -137
  46. {trigger-2.0.0 → trigger-2.0.2}/trigger/netdevices/loader.py +22 -30
  47. {trigger-2.0.0 → trigger-2.0.2}/trigger/netscreen.py +73 -98
  48. {trigger-2.0.0 → trigger-2.0.2}/trigger/rancid.py +51 -60
  49. {trigger-2.0.0 → trigger-2.0.2}/trigger/tacacsrc.py +72 -76
  50. {trigger-2.0.0 → trigger-2.0.2}/trigger/twister.py +165 -243
  51. {trigger-2.0.0 → trigger-2.0.2}/trigger/twister2.py +40 -57
  52. {trigger-2.0.0 → trigger-2.0.2}/trigger/utils/__init__.py +10 -17
  53. {trigger-2.0.0 → trigger-2.0.2}/trigger/utils/cli.py +27 -37
  54. {trigger-2.0.0 → trigger-2.0.2}/trigger/utils/importlib.py +18 -15
  55. {trigger-2.0.0 → trigger-2.0.2}/trigger/utils/network.py +17 -22
  56. {trigger-2.0.0 → trigger-2.0.2}/trigger/utils/rcs.py +14 -23
  57. {trigger-2.0.0 → trigger-2.0.2}/trigger/utils/templates.py +11 -14
  58. {trigger-2.0.0 → trigger-2.0.2}/trigger/utils/url.py +3 -7
  59. {trigger-2.0.0 → trigger-2.0.2}/trigger/utils/xmltodict.py +11 -10
  60. {trigger-2.0.0 → trigger-2.0.2/trigger.egg-info}/PKG-INFO +1 -1
  61. trigger-2.0.0/pyproject.toml +0 -156
  62. {trigger-2.0.0 → trigger-2.0.2}/AUTHORS.md +0 -0
  63. {trigger-2.0.0 → trigger-2.0.2}/LICENSE.md +0 -0
  64. {trigger-2.0.0 → trigger-2.0.2}/README.md +0 -0
  65. {trigger-2.0.0 → trigger-2.0.2}/setup.cfg +0 -0
  66. {trigger-2.0.0 → trigger-2.0.2}/tests/test_acl_db.py +0 -0
  67. {trigger-2.0.0 → trigger-2.0.2}/tests/test_changemgmt.py +0 -0
  68. {trigger-2.0.0 → trigger-2.0.2}/tests/test_twister.py +0 -0
  69. {trigger-2.0.0 → trigger-2.0.2}/tests/test_utils.py +0 -0
  70. {trigger-2.0.0 → trigger-2.0.2}/trigger/__init__.py +0 -0
  71. {trigger-2.0.0 → trigger-2.0.2}/trigger/bin/__init__.py +0 -0
  72. {trigger-2.0.0 → trigger-2.0.2}/trigger/packages/__init__.py +0 -0
  73. {trigger-2.0.0 → trigger-2.0.2}/trigger/packages/peewee.py +0 -0
  74. {trigger-2.0.0 → trigger-2.0.2}/trigger.egg-info/SOURCES.txt +0 -0
  75. {trigger-2.0.0 → trigger-2.0.2}/trigger.egg-info/dependency_links.txt +0 -0
  76. {trigger-2.0.0 → trigger-2.0.2}/trigger.egg-info/entry_points.txt +0 -0
  77. {trigger-2.0.0 → trigger-2.0.2}/trigger.egg-info/requires.txt +0 -0
  78. {trigger-2.0.0 → trigger-2.0.2}/trigger.egg-info/top_level.txt +0 -0
  79. {trigger-2.0.0 → trigger-2.0.2}/twisted/plugins/trigger_xmlrpc.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trigger
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: Network automation toolkit for managing network devices
5
5
  Author-email: Jathan McCollum <jathan@gmail.com>
6
6
  License-Expression: BSD-3-Clause
@@ -0,0 +1,236 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "trigger"
7
+ version = "2.0.2"
8
+ description = "Network automation toolkit for managing network devices"
9
+ readme = "README.md"
10
+ license = "BSD-3-Clause"
11
+ authors = [{name = "Jathan McCollum", email = "jathan@gmail.com"}]
12
+ requires-python = ">=3.10,<3.12"
13
+ classifiers = [
14
+ "Development Status :: 6 - Mature",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Framework :: Twisted",
19
+ ]
20
+ dependencies = [
21
+ "IPy>=1.01",
22
+ "cryptography>=41.0.0",
23
+ "Twisted>=22.10.0",
24
+ "crochet>=2.0.0",
25
+ "pyasn1>=0.4.8",
26
+ "pyparsing>=3.1.0",
27
+ "pytz>=2023.3",
28
+ "SimpleParse>=2.2.0",
29
+ "textfsm>=1.1.0",
30
+ "redis>=5.0.0",
31
+ "PTable>=0.9.2",
32
+ "peewee>=3.17.0",
33
+ "service-identity>=23.1.0",
34
+ "bcrypt>=4.0.0",
35
+ "packaging>=21.0",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ dev = [
40
+ "pytest>=7.4.0",
41
+ "pytest-twisted>=1.14.0",
42
+ "ruff>=0.1.0",
43
+ "python-semantic-release>=9.0.0",
44
+ ]
45
+
46
+ [project.scripts]
47
+ acl = "trigger.bin.acl:main"
48
+ acl_script = "trigger.bin.acl_script:main"
49
+ aclconv = "trigger.bin.aclconv:main"
50
+ check_access = "trigger.bin.check_access:main"
51
+ check_syntax = "trigger.bin.check_syntax:main"
52
+ fe = "trigger.bin.fe:main"
53
+ find_access = "trigger.bin.find_access:main"
54
+ gnng = "trigger.bin.gnng:main"
55
+ gong = "trigger.bin.gong:main"
56
+ load_acl = "trigger.bin.load_acl:main"
57
+ load_config = "trigger.bin.load_config:main"
58
+ netdev = "trigger.bin.netdev:main"
59
+ optimizer = "trigger.bin.optimizer:main"
60
+ run_cmds = "trigger.bin.run_cmds:main"
61
+
62
+ [tool.setuptools]
63
+ packages = ["trigger", "trigger.acl", "trigger.bin", "trigger.changemgmt",
64
+ "trigger.conf", "trigger.contrib", "trigger.netdevices",
65
+ "trigger.packages", "trigger.utils", "twisted.plugins"]
66
+ include-package-data = true
67
+
68
+ [tool.setuptools.package-data]
69
+ twisted = ["plugins/trigger_xmlrpc.py"]
70
+
71
+ # Automatically includes files tracked by git in source distributions
72
+ # This includes: CHANGELOG, *.md, conf/, docs/, examples/, tests/
73
+
74
+ [tool.pytest.ini_options]
75
+ testpaths = ["tests"]
76
+ python_files = ["test_*.py"]
77
+ addopts = "-v"
78
+
79
+ [tool.ruff]
80
+ line-length = 88
81
+ target-version = "py310"
82
+ exclude = ["trigger/packages", ".venv", "build", "dist"]
83
+
84
+ [tool.ruff.lint]
85
+ select = [
86
+ "B", # flake8-bugbear - bug prevention
87
+ "COM", # flake8-commas - trailing comma consistency
88
+ "D", # pydocstyle - docstring formatting
89
+ "E", # pycodestyle errors
90
+ "EM", # flake8-errmsg - exception message hygiene
91
+ "ERA", # eradicate - commented-out code removal
92
+ "F", # pyflakes
93
+ "FURB", # refurb - modern Python idioms
94
+ "I", # isort - import sorting
95
+ "PERF", # perflint - performance patterns
96
+ "PIE", # flake8-pie - unnecessary constructs
97
+ "PLR", # pylint-refactor
98
+ "PLW", # pylint-warnings
99
+ "PT", # flake8-pytest-style
100
+ "PTH", # flake8-use-pathlib
101
+ "RET", # flake8-return
102
+ "RSE", # flake8-raise
103
+ "RUF", # ruff-specific rules
104
+ "S", # flake8-bandit - security
105
+ "SIM", # flake8-simplify
106
+ "UP", # pyupgrade - Python 3 modernization
107
+ "W", # pycodestyle warnings
108
+ ]
109
+ ignore = [
110
+ # --- Formatter conflicts ---
111
+ "COM812", # trailing-comma-missing (handled by ruff formatter)
112
+
113
+ # --- Kept from v2.0.0 baseline ---
114
+ "E402", # module-import-not-at-top-of-file
115
+ "E501", # line-too-long (handled by formatter)
116
+ "E721", # type-comparison (existing code style)
117
+ "E722", # bare-except (intentional in many places)
118
+ "E741", # ambiguous-variable-name (existing code style)
119
+ "F401", # unused-import (may be used dynamically)
120
+ "F402", # import-shadowed-by-loop-var
121
+ "F403", # undefined-local-with-import-star
122
+ "F405", # undefined-local-with-import-star-usage
123
+ "F523", # string-dot-format-extra-positional-arguments
124
+ "F811", # redefined-while-unused
125
+ "F821", # undefined-name
126
+ "UP031", # printf-string-formatting
127
+
128
+ # --- Bugbear exceptions ---
129
+ "B016", # raise-literal (used intentionally in tests)
130
+ "B018", # useless-expression (test assertions)
131
+
132
+ # --- Docstring exceptions (not enforcing coverage/style) ---
133
+ "D100", # undocumented-public-module
134
+ "D101", # undocumented-public-class
135
+ "D102", # undocumented-public-method
136
+ "D103", # undocumented-public-function
137
+ "D104", # undocumented-public-package
138
+ "D105", # undocumented-magic-method
139
+ "D107", # undocumented-public-init
140
+ "D200", # unnecessary-multiline-docstring
141
+ "D205", # missing-blank-line-after-summary
142
+ "D401", # non-imperative-mood
143
+ "D402", # signature-in-docstring
144
+ "D404", # docstring-starts-with-this
145
+
146
+ # --- Complexity (deep refactoring territory) ---
147
+ "PLR0911", # too-many-return-statements
148
+ "PLR0912", # too-many-branches
149
+ "PLR0913", # too-many-arguments
150
+ "PLR0915", # too-many-statements
151
+ "PLR2004", # magic-value-comparison
152
+ "PLW0603", # global-statement (parser pattern)
153
+
154
+ # --- Security false positives for network toolkit ---
155
+ "S101", # assert (used in tests)
156
+ "S105", # hardcoded-password-string (false positives)
157
+ "S110", # try-except-pass
158
+ "S112", # try-except-continue
159
+ "S314", # suspicious-xml-element-tree-usage
160
+ "S603", # subprocess-without-shell-equals-true
161
+ "S605", # start-process-with-a-shell (fix manually)
162
+ "S606", # start-process-with-no-shell
163
+ "S608", # hardcoded-sql-expression
164
+
165
+ # --- Other ---
166
+ "RUF012", # mutable-class-default (too many in device metadata)
167
+ "SLF001", # private-member-access (common in Twisted patterns)
168
+ ]
169
+
170
+ [tool.ruff.lint.pydocstyle]
171
+ convention = "google"
172
+
173
+ [tool.ruff.lint.per-file-ignores]
174
+ "tests/**/*.py" = [
175
+ "ARG", # unused arguments (test fixtures)
176
+ "D", # docstrings not enforced in tests
177
+ "E402", # module-level imports not at top
178
+ "E722", # bare-except
179
+ "E731", # lambda assignments
180
+ "F821", # undefined names (test fixtures)
181
+ "PT009", # pytest-unittest-assertion (allow mixing in tests)
182
+ "PT027", # pytest-unittest-raises-assertion
183
+ "S", # security checks not relevant in tests
184
+ ]
185
+ "tests/acceptance/**/*.py" = [
186
+ "D", # docstrings not enforced
187
+ "E402", # module-level imports not at top
188
+ "E722", # bare-except
189
+ "F821", # undefined names
190
+ "S", # security checks not relevant
191
+ ]
192
+ "trigger/bin/**/*.py" = [
193
+ "S605", # start-process-with-a-shell (CLI tools)
194
+ "S606", # start-process-with-no-shell
195
+ "S607", # start-process-with-partial-path
196
+ ]
197
+ "configs/**/*.py" = [
198
+ "ERA001", # commented-out code (serves as user documentation/examples)
199
+ "PTH", # pathlib (settings values are strings by design)
200
+ ]
201
+ "docs/conf.py" = [
202
+ "D", # docstrings not enforced
203
+ "E402", # module-level imports not at top
204
+ "F401", # unused imports (used by Sphinx)
205
+ ]
206
+
207
+ [tool.ruff.lint.isort]
208
+ known-first-party = ["trigger"]
209
+
210
+ [tool.ruff.format]
211
+ quote-style = "double"
212
+ indent-style = "space"
213
+ skip-magic-trailing-comma = false
214
+ line-ending = "auto"
215
+
216
+ [tool.semantic_release]
217
+ version_toml = ["pyproject.toml:project.version"]
218
+ branch = "main"
219
+ upload_to_pypi = true
220
+ upload_to_release = true
221
+ build_command = "pip install uv && uv build"
222
+ major_on_zero = false
223
+ tag_format = "v{version}"
224
+
225
+ [tool.semantic_release.commit_parser_options]
226
+ allowed_tags = ["feat", "fix", "perf", "refactor", "docs", "chore", "ci", "test", "build"]
227
+ major_tags = ["BREAKING CHANGE"]
228
+ minor_tags = ["feat"]
229
+ patch_tags = ["fix", "perf"]
230
+
231
+ [tool.semantic_release.changelog]
232
+ changelog_file = "CHANGELOG.md"
233
+
234
+ [tool.semantic_release.branches.main]
235
+ match = "main"
236
+ prerelease = false
@@ -5,8 +5,10 @@ __maintainer__ = "Jathan McCollum"
5
5
  __copyright__ = "Copyright 2005-2011 AOL Inc.; 2013 Salesforce.com"
6
6
  __version__ = "2.0"
7
7
 
8
+ import contextlib
8
9
  import unittest
9
10
  from io import StringIO
11
+ from pathlib import Path
10
12
 
11
13
  from trigger import acl, exceptions
12
14
 
@@ -91,7 +93,7 @@ class CheckACLNames(unittest.TestCase):
91
93
  for name in ("", "x" * 25):
92
94
  try:
93
95
  acl.ACL(name=name)
94
- except exceptions.ACLNameError:
96
+ except exceptions.ACLNameError: # noqa: PERF203
95
97
  pass
96
98
  else:
97
99
  self.fail('expected ACLNameError on "' + name + '"')
@@ -131,7 +133,7 @@ class CheckTerms(unittest.TestCase):
131
133
  for name in ("", "x" * 300):
132
134
  try:
133
135
  acl.Term(name=name)
134
- except exceptions.BadTermName:
136
+ except exceptions.BadTermName: # noqa: PERF203
135
137
  pass
136
138
  else:
137
139
  self.fail('expected BadTermNameon "' + name + '"')
@@ -170,10 +172,10 @@ class CheckTerms(unittest.TestCase):
170
172
  ):
171
173
  try:
172
174
  acl.Term(action=action)
173
- except exceptions.ActionError:
175
+ except exceptions.ActionError: # noqa: PERF203
174
176
  pass
175
177
  else:
176
- self.fail(f'expected ActionError on "{str(action)}"')
178
+ self.fail(f'expected ActionError on "{action!s}"')
177
179
 
178
180
  def testOkModifiers(self):
179
181
  """Test valid filter action modifiers"""
@@ -210,10 +212,10 @@ class CheckTerms(unittest.TestCase):
210
212
  ):
211
213
  try:
212
214
  acl.Term(action=action)
213
- except exceptions.ActionError:
215
+ except exceptions.ActionError: # noqa: PERF203
214
216
  pass
215
217
  else:
216
- self.fail(f'expected ActionError on "{str(action)}"')
218
+ self.fail(f'expected ActionError on "{action!s}"')
217
219
 
218
220
  def testOkMatches(self):
219
221
  """Test valid match conditions"""
@@ -307,7 +309,7 @@ class CheckOutput(unittest.TestCase):
307
309
  self.t2.match["protocol"] = ["tcp"]
308
310
  self.t2.match["source-address"] = ["192.0.2.0/24"]
309
311
  # Python 3: range() returns a range object, convert to list for concatenation
310
- self.t2.match["destination-port"] = list(range(135, 139)) + [445]
312
+ self.t2.match["destination-port"] = [*list(range(135, 139)), 445]
311
313
  self.t2.action = "reject"
312
314
  self.t2.modifiers["syslog"] = True
313
315
  self.a.terms.append(self.t2)
@@ -346,10 +348,8 @@ filter 100j {
346
348
  def testIOS(self):
347
349
  """Test conversion of ACLs and terms to IOS classic format"""
348
350
  self.a.name = 100
349
- try:
351
+ with contextlib.suppress(KeyError):
350
352
  del self.t1.modifiers["count"]
351
- except KeyError:
352
- pass
353
353
  output = """\
354
354
  access-list 100 permit 99 any any
355
355
  access-list 100 deny tcp 192.0.2.0 0.0.0.255 any range 135 138 log
@@ -359,10 +359,8 @@ access-list 100 deny tcp 192.0.2.0 0.0.0.255 any eq 445 log"""
359
359
  def testIOSExtended(self):
360
360
  """Test conversion of ACLs and terms to IOS extended format"""
361
361
  self.a.name = "BLAHBLAH"
362
- try:
362
+ with contextlib.suppress(KeyError):
363
363
  del self.t1.modifiers["count"]
364
- except KeyError:
365
- pass
366
364
  output = """\
367
365
  ip access-list extended BLAHBLAH
368
366
  permit 99 any any
@@ -373,10 +371,8 @@ ip access-list extended BLAHBLAH
373
371
  def testIOSXR(self):
374
372
  """Test conversion of ACLs and terms to IOS XR format"""
375
373
  self.a.name = "BLAHBLAH"
376
- try:
374
+ with contextlib.suppress(KeyError):
377
375
  del self.t1.modifiers["count"]
378
- except KeyError:
379
- pass
380
376
  self.t1.name = self.t2.name = None
381
377
  output = """\
382
378
  ipv4 access-list BLAHBLAH
@@ -475,7 +471,7 @@ class CheckJunOSExamples(unittest.TestCase):
475
471
  def testJunOSExamples(self):
476
472
  """Test examples from JunOS documentation."""
477
473
  # Python 3: file() removed, use open()
478
- with open(EXAMPLES_FILE) as f:
474
+ with Path(EXAMPLES_FILE).open() as f:
479
475
  examples = f.read().expandtabs().split("\n\n")
480
476
  # Skip the last two because they use the unimplemented "except"
481
477
  # feature in address matches.
@@ -602,10 +598,6 @@ filter 100 {
602
598
  }
603
599
  }"""
604
600
  self.assertRaises(exceptions.ParserSyntaxError, lambda: acl.parse(x))
605
- ###y = ['access-list 100 permit tcp any any eq 80']
606
- ###a = acl.parse(x)
607
- ###a.comments = a.terms[0].comments = []
608
- ###self.assertEqual(a.output_ios(), y)
609
601
 
610
602
  def testRanges(self):
611
603
  """Test JunOS ICMP and protocol ranges (regression)."""
@@ -702,7 +694,8 @@ class CheckMiscIOS(unittest.TestCase):
702
694
  t.match["icmp-type"] = types
703
695
  # Python 3: map() returns an iterator, convert to list for comparison
704
696
  self.assertEqual(
705
- t.output_ios(), list(map(lambda x: "permit icmp any any %d" % x, types))
697
+ t.output_ios(),
698
+ list(map(lambda x: "permit icmp any any %d" % x, types)),
706
699
  )
707
700
 
708
701
  def testCounterSuppression(self):
@@ -8,6 +8,7 @@ Only tests SQLite for now.
8
8
 
9
9
  import os
10
10
  import tempfile
11
+ from pathlib import Path
11
12
 
12
13
  from trigger.conf import settings
13
14
 
@@ -79,7 +80,10 @@ class TestAclQueue(unittest.TestCase):
79
80
  def test_03_insert_integrated_failure_acl(self):
80
81
  """Test insert devices w/ no ACL association"""
81
82
  self.assertRaises(
82
- exceptions.TriggerError, self.q.insert, "bogus", self.device_list
83
+ exceptions.TriggerError,
84
+ self.q.insert,
85
+ "bogus",
86
+ self.device_list,
83
87
  )
84
88
 
85
89
  def test_04_list_integrated_success(self):
@@ -153,7 +157,7 @@ class TestAclQueue(unittest.TestCase):
153
157
 
154
158
  def test_ZZ_cleanup_db(self):
155
159
  """Cleanup the temp database file"""
156
- self.assertTrue(os.remove(db_file) is None)
160
+ self.assertTrue(Path(db_file).unlink() is None)
157
161
 
158
162
  def tearDown(self):
159
163
  NetDevices._Singleton = None
@@ -1,8 +1,9 @@
1
1
  # a test for except processing
2
2
  import os
3
3
  import sys
4
+ from pathlib import Path
4
5
 
5
- sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), "..")))
6
+ sys.path.append(str(Path(__file__).parent / ".."))
6
7
  from trigger import acl
7
8
 
8
9
  PARSIT = """
@@ -33,4 +34,3 @@ y = acl.parse(PARSIT)
33
34
  print(y.terms[0].match)
34
35
  print("\n".join(acl.parse(PARSIT).output_junos()))
35
36
  # following should fail
36
- # print('\n'.join(acl.parse(PARSIT).output_ios()))
@@ -129,8 +129,8 @@ class TestNetDevicesWithoutAcls(unittest.TestCase):
129
129
  def setUp(self):
130
130
  self.nd = NetDevices(with_acls=False)
131
131
  # Python 3: dict.keys() and dict.values() return views, convert to list for subscripting
132
- self.nodename = list(self.nd.keys())[0]
133
- self.device = list(self.nd.values())[0]
132
+ self.nodename = next(iter(self.nd.keys()))
133
+ self.device = next(iter(self.nd.values()))
134
134
 
135
135
  def test_aclsdb(self):
136
136
  """Test acls.db handling."""
@@ -173,12 +173,10 @@ class TestNetDeviceObject(unittest.TestCase):
173
173
  def test_allowable(self):
174
174
  """Test allowable() method"""
175
175
  # This is already tested in test_changemgmt.py, so this is a stub.
176
- pass
177
176
 
178
177
  def test_next_ok(self):
179
178
  """Test next_ok() method"""
180
179
  # This is already tested in test_changemgmt.py, so this is a stub.
181
- pass
182
180
 
183
181
  def test_identity(self):
184
182
  """Exercise NetDevice identity tests."""
@@ -207,7 +205,7 @@ class TestNetDeviceObject(unittest.TestCase):
207
205
 
208
206
  def test_dump(self):
209
207
  """Test the dump() method."""
210
- with captured_output() as (out, err):
208
+ with captured_output() as (out, _err):
211
209
  self.device.dump()
212
210
  expected = NETDEVICE_DUMP_EXPECTED
213
211
  output = out.getvalue()
@@ -254,9 +252,9 @@ class TestVendorObject(unittest.TestCase):
254
252
  def test_membership(self):
255
253
  """Test membership w/ __eq__ and __contains__"""
256
254
  expected = "cisco"
257
- self.assertTrue(expected in [self.vendor])
258
- self.assertTrue(self.vendor in [self.vendor])
259
- self.assertTrue(self.vendor in [expected])
255
+ self.assertTrue(expected == self.vendor)
256
+ self.assertTrue(self.vendor == self.vendor)
257
+ self.assertTrue(self.vendor == expected)
260
258
  self.assertFalse(self.vendor in ["juniper", "foundry"])
261
259
 
262
260
  def test_determine_vendor(self):
@@ -10,10 +10,11 @@ __version__ = "1.1"
10
10
  import os
11
11
  import subprocess
12
12
  import unittest
13
+ from pathlib import Path
13
14
 
14
15
  ACLCONV = "aclconv" # Now an entry point, not a script in bin/
15
16
 
16
- os.environ["PYTHONPATH"] = os.getcwd()
17
+ os.environ["PYTHONPATH"] = str(Path.cwd())
17
18
 
18
19
  # TODO (jathan): Add tests for all the scripts!!
19
20
 
@@ -30,7 +31,7 @@ class Aclconv(unittest.TestCase):
30
31
  stderr=subprocess.PIPE,
31
32
  text=True,
32
33
  )
33
- output, errors = proc.communicate("access-list 100 deny ip any any")
34
+ output, _errors = proc.communicate("access-list 100 deny ip any any")
34
35
  self.assertEqual(proc.returncode, 0)
35
36
  correct_output = """\
36
37
  firewall {
@@ -9,6 +9,7 @@ __version__ = "2.0.1"
9
9
  import os
10
10
  import tempfile
11
11
  import unittest
12
+ from pathlib import Path
12
13
  from unittest.mock import patch
13
14
 
14
15
  from trigger.conf import settings
@@ -89,7 +90,7 @@ class TacacsrcTest(unittest.TestCase):
89
90
  def _get_perms(self, filename):
90
91
  """Get octal permissions for a filename"""
91
92
  # We only want the lower 4 bits (negative index)
92
- return oct(os.stat(filename).st_mode)[-4:]
93
+ return oct(Path(filename).stat().st_mode)[-4:]
93
94
 
94
95
  def testWrite(self):
95
96
  """Test writing .tacacsrc."""
@@ -110,26 +111,30 @@ class TacacsrcTest(unittest.TestCase):
110
111
  self.assertEqual(output, ALL_TACACSRC)
111
112
 
112
113
  # And then compare it against the manually parsed value using
113
- # miniparser()
114
- with open(settings.TACACSRC) as fd:
114
+ # miniparser() # noqa: ERA001
115
+ with Path(settings.TACACSRC).open() as fd:
115
116
  lines = fd.readlines()
116
117
  self.assertEqual(output, miniparser(lines, t))
117
- os.remove(file_name)
118
+ Path(file_name).unlink()
118
119
 
119
120
  def test_brokenpw(self):
120
121
  self.assertRaises(
121
- ValueError, MockTacacsrc, tacacsrc_file="tests/data/brokenpw_tacacsrc"
122
+ ValueError,
123
+ MockTacacsrc,
124
+ tacacsrc_file="tests/data/brokenpw_tacacsrc",
122
125
  )
123
126
 
124
127
  def test_emptypw(self):
125
- devnull = open(os.devnull, "w")
126
- with patch("trigger.tacacsrc.prompt_credentials", side_effect=KeyError):
127
- with patch("sys.stdout", devnull):
128
- self.assertRaises(
129
- KeyError,
130
- MockTacacsrc,
131
- tacacsrc_file="tests/data/emptypw_tacacsrc",
132
- )
128
+ with (
129
+ Path(os.devnull).open("w") as devnull,
130
+ patch("trigger.tacacsrc.prompt_credentials", side_effect=KeyError),
131
+ patch("sys.stdout", devnull),
132
+ ):
133
+ self.assertRaises(
134
+ KeyError,
135
+ MockTacacsrc,
136
+ tacacsrc_file="tests/data/emptypw_tacacsrc",
137
+ )
133
138
 
134
139
  def test_perms(self):
135
140
  """Test that permissions are being enforced."""
@@ -138,7 +143,7 @@ class TacacsrcTest(unittest.TestCase):
138
143
  # First make sure perms are set
139
144
  old_perms = self._get_perms(fname)
140
145
  self.assertEqual(old_perms, RIGHT_PERMS)
141
- os.chmod(fname, 0o666) # Make it world-writable
146
+ Path(fname).chmod(0o666) # Make it world-writable
142
147
  new_perms = self._get_perms(fname)
143
148
  self.assertNotEqual(new_perms, RIGHT_PERMS)
144
149
 
@@ -128,7 +128,9 @@ class CheckTemplates(unittest.TestCase):
128
128
  commands = ["show version"]
129
129
  commando = Commando(devices=[self.device.nodeName])
130
130
  data = commando.parse_template(
131
- results=[big_cli_data], device=self.device, commands=commands
131
+ results=[big_cli_data],
132
+ device=self.device,
133
+ commands=commands,
132
134
  )
133
135
  self.assertTrue(len(data) > 0)
134
136
  self.assertTrue(isinstance(data, list))
@@ -143,7 +145,9 @@ class CheckTemplates(unittest.TestCase):
143
145
  commands = ["show run | in cisco"]
144
146
  commando = Commando(devices=[self.device.nodeName])
145
147
  data = commando.parse_template(
146
- results=[no_template_data], device=self.device, commands=commands
148
+ results=[no_template_data],
149
+ device=self.device,
150
+ commands=commands,
147
151
  )
148
152
  self.assertTrue(len(data) > 0)
149
153
  self.assertTrue(isinstance(data, list))
@@ -13,8 +13,4 @@ __copyright__ = "Copyright 2005-2011 AOL Inc.; 2013 Salesforce.com.; 2016 Dropbo
13
13
  __version__ = "1.0"
14
14
 
15
15
 
16
- # from utils import captured_output
17
-
18
- # Now we can import from Trigger
19
- # from trigger.netdevices import NetDevices, NetDevice, Vendor
20
16
  # TODO: see http://twistedmatrix.com/trac/wiki/TwistedTrial
@@ -1,5 +1,4 @@
1
- """
2
- Trigger's ACL parser.
1
+ """Trigger's ACL parser.
3
2
 
4
3
  This library contains various modules that allow for parsing, manipulation,
5
4
  and management of network access control lists (ACLs). It will parse a complete
@@ -18,7 +17,7 @@ import os
18
17
 
19
18
  from trigger.conf import settings
20
19
 
21
- __all__ = ["parser", "acl_exists"]
20
+ __all__ = ["acl_exists", "parser"]
22
21
 
23
22
  # Parser
24
23
  from . import parser
@@ -1,5 +1,4 @@
1
- """
2
- This module controls when ACLs get auto-applied to network devices,
1
+ """This module controls when ACLs get auto-applied to network devices,
3
2
  in addition to what is specified in acls.db.
4
3
 
5
4
  This is primarily used by :class:`~trigger.acl.db.AclsDB` to populate the
@@ -44,11 +43,10 @@ try:
44
43
  from _autoacl_module import autoacl
45
44
  except ImportError:
46
45
  msg = f"Function autoacl() could not be found in {module_path}, using default!"
47
- warnings.warn(msg, RuntimeWarning)
46
+ warnings.warn(msg, RuntimeWarning, stacklevel=2)
48
47
 
49
48
  def autoacl(dev, explicit_acls=None):
50
- """
51
- Given a NetDevice object, returns a set of **implicit** (auto) ACLs. We
49
+ """Given a NetDevice object, returns a set of **implicit** (auto) ACLs. We
52
50
  require a device object so that we don't have circular dependencies
53
51
  between netdevices and autoacl.
54
52