flake8-author 2.0.0__tar.gz → 2.2.0__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.
@@ -1,30 +1,32 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: flake8-author
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: Flake8 __author__ checker
5
- Home-page: https://github.com/jparise/flake8-author
6
- Author: Jon Parise
7
- Author-email: jon@indelible.org
8
- License: MIT
9
- Project-URL: Source Code, https://github.com/jparise/flake8-author
10
- Project-URL: Issue Tracker, https://github.com/jparise/flake8-author/issues
11
- Keywords: flake8 __author__
12
- Platform: UNKNOWN
5
+ Author-email: Jon Parise <jon@indelible.org>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jparise/flake8-author
8
+ Project-URL: Repository, https://github.com/jparise/flake8-author
9
+ Project-URL: Issues, https://github.com/jparise/flake8-author/issues
10
+ Project-URL: Changelog, https://github.com/jparise/flake8-author/blob/main/CHANGELOG.rst
11
+ Keywords: flake8,__author__
13
12
  Classifier: Development Status :: 5 - Production/Stable
14
13
  Classifier: Environment :: Console
15
14
  Classifier: Framework :: Flake8
16
15
  Classifier: Intended Audience :: Developers
17
- Classifier: License :: OSI Approved :: MIT License
18
16
  Classifier: Programming Language :: Python
19
17
  Classifier: Programming Language :: Python :: 3 :: Only
20
- Classifier: Programming Language :: Python :: 3.7
21
- Classifier: Programming Language :: Python :: 3.8
22
18
  Classifier: Programming Language :: Python :: 3.9
23
19
  Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
24
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
24
  Classifier: Topic :: Software Development :: Quality Assurance
26
- Requires-Python: >=3.7
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/x-rst
27
27
  License-File: LICENSE
28
+ Requires-Dist: flake8>=3.0.0
29
+ Dynamic: license-file
28
30
 
29
31
  =========================
30
32
  Flake8 __author__ Checker
@@ -51,6 +53,9 @@ This extension can therefore be configured to explicitly require or forbid
51
53
  If the ``__author__`` attribute is allowed, its value can also be validated
52
54
  using a configurable regular expression pattern.
53
55
 
56
+ The ``__author__`` attribute is generally assigned to a string, but multiple
57
+ authors can be listed using a list (or tuple or set) of strings.
58
+
54
59
  .. |PyPI Version| image:: https://img.shields.io/pypi/v/flake8-author.svg
55
60
  :target: https://pypi.python.org/pypi/flake8-author
56
61
  .. |Python Versions| image:: https://img.shields.io/pypi/pyversions/flake8-author.svg
@@ -72,7 +77,7 @@ that it has been loaded by inspecting the ``flake8 --version`` string.
72
77
  .. code-block:: sh
73
78
 
74
79
  $ flake8 --version
75
- 4.0.1 (author: 2.0.0, ...) CPython 3.9.9 on Darwin
80
+ 6.1.0 (author: 2.1.0, ...) CPython 3.11.5 on Darwin
76
81
 
77
82
 
78
83
  Error Codes
@@ -97,85 +102,3 @@ file`_ or as command line arguments (e.g. ``--author-attribute=required``).
97
102
 
98
103
  .. _config file: http://flake8.pycqa.org/en/latest/user/configuration.html
99
104
  .. _re: https://docs.python.org/library/re.html
100
-
101
- Copyright (c) 2016 Jon Parise
102
-
103
- Permission is hereby granted, free of charge, to any person obtaining a copy
104
- of this software and associated documentation files (the "Software"), to deal
105
- in the Software without restriction, including without limitation the rights
106
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
107
- copies of the Software, and to permit persons to whom the Software is
108
- furnished to do so, subject to the following conditions:
109
-
110
- The above copyright notice and this permission notice shall be included in all
111
- copies or substantial portions of the Software.
112
-
113
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
114
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
115
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
116
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
117
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
118
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
119
- SOFTWARE.
120
-
121
- Changes
122
- =======
123
-
124
- 2.0.0 (2021-12-30)
125
- ------------------
126
-
127
- * Drop support for Python 2.7 and 3.6, and add 3.10.
128
- * Drop support for flake8 2.x versions.
129
-
130
- 1.2.0 (2020-10-12)
131
- ------------------
132
-
133
- * Drop Python 3.5 support and add versions 3.8 and 3.9.
134
- * Minor documentation improvements.
135
-
136
- 1.1.4 (2017-07-29)
137
- ------------------
138
-
139
- * Minor code and packaging cleanup.
140
-
141
- 1.1.3 (2017-07-28)
142
- ------------------
143
-
144
- * Drop the ``setup_requires`` dependency on flake8.
145
-
146
- 1.1.2 (2017-01-12)
147
- ------------------
148
-
149
- * Only apply the author regular expression pattern check when a pattern has
150
- actually been configured.
151
-
152
- 1.1.1 (2016-11-06)
153
- ------------------
154
-
155
- * Fix configuration file parsing for flake8 3.0+.
156
-
157
- 1.1.0 (2016-08-15)
158
- ------------------
159
-
160
- * Support flake8 3.0.0 (required for development)
161
-
162
-
163
- 1.0.2 (2016-06-02)
164
- ------------------
165
-
166
- * Support flake8 version 2.6.0
167
- * Drop test dependency on pytest (development only)
168
-
169
-
170
- 1.0.1 (2016-05-11)
171
- ------------------
172
-
173
- * Fixed parser validation for configuration-based options
174
-
175
-
176
- 1.0.0 (2016-04-30)
177
- ------------------
178
-
179
- * Initial release
180
-
181
-
@@ -23,6 +23,9 @@ This extension can therefore be configured to explicitly require or forbid
23
23
  If the ``__author__`` attribute is allowed, its value can also be validated
24
24
  using a configurable regular expression pattern.
25
25
 
26
+ The ``__author__`` attribute is generally assigned to a string, but multiple
27
+ authors can be listed using a list (or tuple or set) of strings.
28
+
26
29
  .. |PyPI Version| image:: https://img.shields.io/pypi/v/flake8-author.svg
27
30
  :target: https://pypi.python.org/pypi/flake8-author
28
31
  .. |Python Versions| image:: https://img.shields.io/pypi/pyversions/flake8-author.svg
@@ -44,7 +47,7 @@ that it has been loaded by inspecting the ``flake8 --version`` string.
44
47
  .. code-block:: sh
45
48
 
46
49
  $ flake8 --version
47
- 4.0.1 (author: 2.0.0, ...) CPython 3.9.9 on Darwin
50
+ 6.1.0 (author: 2.1.0, ...) CPython 3.11.5 on Darwin
48
51
 
49
52
 
50
53
  Error Codes
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "flake8-author"
7
+ dynamic = ["version"]
8
+ description = "Flake8 __author__ checker"
9
+ authors = [
10
+ {name = "Jon Parise", email = "jon@indelible.org"},
11
+ ]
12
+ readme = "README.rst"
13
+ license = "MIT"
14
+ keywords = ["flake8", "__author__"]
15
+ classifiers = [
16
+ "Development Status :: 5 - Production/Stable",
17
+ "Environment :: Console",
18
+ "Framework :: Flake8",
19
+ "Intended Audience :: Developers",
20
+ "Programming Language :: Python",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Software Development :: Libraries :: Python Modules",
28
+ "Topic :: Software Development :: Quality Assurance",
29
+ ]
30
+ requires-python = ">= 3.9"
31
+ dependencies = [
32
+ "flake8>=3.0.0",
33
+ ]
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ "pytest>=8",
38
+ "ruff==0.12.0",
39
+ ]
40
+
41
+ [project.entry-points."flake8.extension"]
42
+ A40 = "flake8_author:Checker"
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/jparise/flake8-author"
46
+ Repository = "https://github.com/jparise/flake8-author"
47
+ Issues = "https://github.com/jparise/flake8-author/issues"
48
+ Changelog = "https://github.com/jparise/flake8-author/blob/main/CHANGELOG.rst"
49
+
50
+ [tool.setuptools.dynamic]
51
+ version = {attr = "flake8_author.__version__"}
@@ -0,0 +1,9 @@
1
+ [flake8]
2
+ max-line-length = 88
3
+ ignore = E203,E701
4
+ exclude = .eggs/,.git/,.venv/,build/,dist/
5
+
6
+ [egg_info]
7
+ tag_build =
8
+ tag_date = 0
9
+
@@ -1,30 +1,32 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: flake8-author
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: Flake8 __author__ checker
5
- Home-page: https://github.com/jparise/flake8-author
6
- Author: Jon Parise
7
- Author-email: jon@indelible.org
8
- License: MIT
9
- Project-URL: Source Code, https://github.com/jparise/flake8-author
10
- Project-URL: Issue Tracker, https://github.com/jparise/flake8-author/issues
11
- Keywords: flake8 __author__
12
- Platform: UNKNOWN
5
+ Author-email: Jon Parise <jon@indelible.org>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jparise/flake8-author
8
+ Project-URL: Repository, https://github.com/jparise/flake8-author
9
+ Project-URL: Issues, https://github.com/jparise/flake8-author/issues
10
+ Project-URL: Changelog, https://github.com/jparise/flake8-author/blob/main/CHANGELOG.rst
11
+ Keywords: flake8,__author__
13
12
  Classifier: Development Status :: 5 - Production/Stable
14
13
  Classifier: Environment :: Console
15
14
  Classifier: Framework :: Flake8
16
15
  Classifier: Intended Audience :: Developers
17
- Classifier: License :: OSI Approved :: MIT License
18
16
  Classifier: Programming Language :: Python
19
17
  Classifier: Programming Language :: Python :: 3 :: Only
20
- Classifier: Programming Language :: Python :: 3.7
21
- Classifier: Programming Language :: Python :: 3.8
22
18
  Classifier: Programming Language :: Python :: 3.9
23
19
  Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
24
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
24
  Classifier: Topic :: Software Development :: Quality Assurance
26
- Requires-Python: >=3.7
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/x-rst
27
27
  License-File: LICENSE
28
+ Requires-Dist: flake8>=3.0.0
29
+ Dynamic: license-file
28
30
 
29
31
  =========================
30
32
  Flake8 __author__ Checker
@@ -51,6 +53,9 @@ This extension can therefore be configured to explicitly require or forbid
51
53
  If the ``__author__`` attribute is allowed, its value can also be validated
52
54
  using a configurable regular expression pattern.
53
55
 
56
+ The ``__author__`` attribute is generally assigned to a string, but multiple
57
+ authors can be listed using a list (or tuple or set) of strings.
58
+
54
59
  .. |PyPI Version| image:: https://img.shields.io/pypi/v/flake8-author.svg
55
60
  :target: https://pypi.python.org/pypi/flake8-author
56
61
  .. |Python Versions| image:: https://img.shields.io/pypi/pyversions/flake8-author.svg
@@ -72,7 +77,7 @@ that it has been loaded by inspecting the ``flake8 --version`` string.
72
77
  .. code-block:: sh
73
78
 
74
79
  $ flake8 --version
75
- 4.0.1 (author: 2.0.0, ...) CPython 3.9.9 on Darwin
80
+ 6.1.0 (author: 2.1.0, ...) CPython 3.11.5 on Darwin
76
81
 
77
82
 
78
83
  Error Codes
@@ -97,85 +102,3 @@ file`_ or as command line arguments (e.g. ``--author-attribute=required``).
97
102
 
98
103
  .. _config file: http://flake8.pycqa.org/en/latest/user/configuration.html
99
104
  .. _re: https://docs.python.org/library/re.html
100
-
101
- Copyright (c) 2016 Jon Parise
102
-
103
- Permission is hereby granted, free of charge, to any person obtaining a copy
104
- of this software and associated documentation files (the "Software"), to deal
105
- in the Software without restriction, including without limitation the rights
106
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
107
- copies of the Software, and to permit persons to whom the Software is
108
- furnished to do so, subject to the following conditions:
109
-
110
- The above copyright notice and this permission notice shall be included in all
111
- copies or substantial portions of the Software.
112
-
113
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
114
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
115
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
116
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
117
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
118
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
119
- SOFTWARE.
120
-
121
- Changes
122
- =======
123
-
124
- 2.0.0 (2021-12-30)
125
- ------------------
126
-
127
- * Drop support for Python 2.7 and 3.6, and add 3.10.
128
- * Drop support for flake8 2.x versions.
129
-
130
- 1.2.0 (2020-10-12)
131
- ------------------
132
-
133
- * Drop Python 3.5 support and add versions 3.8 and 3.9.
134
- * Minor documentation improvements.
135
-
136
- 1.1.4 (2017-07-29)
137
- ------------------
138
-
139
- * Minor code and packaging cleanup.
140
-
141
- 1.1.3 (2017-07-28)
142
- ------------------
143
-
144
- * Drop the ``setup_requires`` dependency on flake8.
145
-
146
- 1.1.2 (2017-01-12)
147
- ------------------
148
-
149
- * Only apply the author regular expression pattern check when a pattern has
150
- actually been configured.
151
-
152
- 1.1.1 (2016-11-06)
153
- ------------------
154
-
155
- * Fix configuration file parsing for flake8 3.0+.
156
-
157
- 1.1.0 (2016-08-15)
158
- ------------------
159
-
160
- * Support flake8 3.0.0 (required for development)
161
-
162
-
163
- 1.0.2 (2016-06-02)
164
- ------------------
165
-
166
- * Support flake8 version 2.6.0
167
- * Drop test dependency on pytest (development only)
168
-
169
-
170
- 1.0.1 (2016-05-11)
171
- ------------------
172
-
173
- * Fixed parser validation for configuration-based options
174
-
175
-
176
- 1.0.0 (2016-04-30)
177
- ------------------
178
-
179
- * Initial release
180
-
181
-
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.rst
3
+ pyproject.toml
4
+ setup.cfg
5
+ src/flake8_author.py
6
+ src/flake8_author.egg-info/PKG-INFO
7
+ src/flake8_author.egg-info/SOURCES.txt
8
+ src/flake8_author.egg-info/dependency_links.txt
9
+ src/flake8_author.egg-info/entry_points.txt
10
+ src/flake8_author.egg-info/requires.txt
11
+ src/flake8_author.egg-info/top_level.txt
12
+ tests/test_checker.py
@@ -1,3 +1,2 @@
1
1
  [flake8.extension]
2
2
  A40 = flake8_author:Checker
3
-
@@ -0,0 +1,106 @@
1
+ """Flake8 extension that checks Python modules for an __author__ attribute.
2
+
3
+ This extension can be configured to explicitly require or forbid __author__
4
+ attributes. By default, __author__ is optional.
5
+
6
+ If the __author__ attribute is allowed, its value will be validated against a
7
+ configurable regular expression pattern (defaults to '.*').
8
+ """
9
+
10
+ import ast
11
+ import re
12
+ from typing import Literal, Optional, get_args
13
+
14
+ __author__ = "Jon Parise"
15
+ __version__ = "2.2.0"
16
+
17
+ AttributePolicy = Literal["optional", "required", "forbidden"]
18
+
19
+
20
+ class Checker(object):
21
+ """flake8 __author__ checker"""
22
+
23
+ name = "author"
24
+ version = __version__
25
+
26
+ # Options
27
+ attribute: AttributePolicy = "optional"
28
+ pattern: Optional[re.Pattern] = None
29
+
30
+ def __init__(self, tree, filename):
31
+ self.tree = tree
32
+
33
+ @classmethod
34
+ def add_options(cls, parser):
35
+ extra_kwargs = {"parse_from_config": True}
36
+
37
+ parser.add_option(
38
+ "--author-attribute",
39
+ default=cls.attribute,
40
+ help="__author__ attribute: {0}".format(
41
+ ", ".join(get_args(AttributePolicy))
42
+ ),
43
+ **extra_kwargs,
44
+ )
45
+ parser.add_option(
46
+ "--author-pattern",
47
+ default=r"",
48
+ help="__author__ attribute validation pattern (regex)",
49
+ **extra_kwargs,
50
+ )
51
+
52
+ @classmethod
53
+ def parse_options(cls, options):
54
+ if options.author_attribute in get_args(AttributePolicy):
55
+ cls.attribute = options.author_attribute
56
+ else:
57
+ raise ValueError(
58
+ "author-attribute: '{0}' must be one of: {1}".format(
59
+ options.author_attribute, ", ".join(get_args(AttributePolicy))
60
+ )
61
+ )
62
+
63
+ # Only build a regular expression object when we've been configured
64
+ # with a pattern that doesn't match all strings. This naively just
65
+ # checks for the default empty pattern string as well as `.*`.
66
+ if options.author_pattern and options.author_pattern != ".*":
67
+ try:
68
+ cls.pattern = re.compile(options.author_pattern)
69
+ except re.error as e:
70
+ raise ValueError(
71
+ "author-pattern: '{0}': {1}".format(options.author_pattern, e)
72
+ )
73
+
74
+ def find_author_node(self, tree):
75
+ for node in tree.body:
76
+ if not isinstance(node, ast.Assign):
77
+ continue
78
+
79
+ for target in node.targets:
80
+ if isinstance(target, ast.Name) and target.id == "__author__":
81
+ return node
82
+
83
+ def _match_author_pattern(self, author, node):
84
+ if self.pattern is not None and not self.pattern.match(author):
85
+ message = 'A402 __author__ value "{0}" does not match "{1}"'.format(
86
+ author, self.pattern.pattern
87
+ )
88
+ yield (node.lineno, node.col_offset, message, type(self))
89
+
90
+ def run(self):
91
+ node = self.find_author_node(self.tree)
92
+
93
+ if node is None and self.attribute == "required":
94
+ message = "A400 module-level __author__ attribute required"
95
+ yield 0, 0, message, type(self)
96
+
97
+ elif node and self.attribute == "forbidden":
98
+ message = "A401 __author__ attributes are not allowed"
99
+ yield node.lineno, node.col_offset, message, type(self)
100
+
101
+ elif node and self.pattern is not None:
102
+ if isinstance(node.value, (ast.List, ast.Tuple, ast.Set)):
103
+ for author in ast.literal_eval(node.value):
104
+ yield from self._match_author_pattern(author, node)
105
+ elif isinstance(node.value, ast.Constant):
106
+ yield from self._match_author_pattern(node.value.value, node)
@@ -0,0 +1,197 @@
1
+ import ast
2
+ import unittest
3
+
4
+ from flake8.main.application import Application
5
+ from flake8_author import Checker
6
+
7
+
8
+ def make_linter(code, path="example.py", argv=None):
9
+ app = Application()
10
+ app.initialize(argv)
11
+ Checker.parse_options(app.options)
12
+ tree = ast.parse(code, path)
13
+ return Checker(tree, path)
14
+
15
+
16
+ def check(author, attribute=None, pattern=None):
17
+ code = ""
18
+ if isinstance(author, (list, tuple, set)):
19
+ code = "__author__ = {0}".format(author)
20
+ elif author:
21
+ code = '__author__ = "{0}"'.format(author)
22
+
23
+ argv = []
24
+ if attribute:
25
+ argv.append("--author-attribute={0}".format(attribute))
26
+ if pattern:
27
+ argv.append("--author-pattern={0}".format(pattern))
28
+
29
+ linter = make_linter(code, argv=argv)
30
+ if isinstance(author, (list, tuple, set)):
31
+ result = list(linter.run())
32
+ return result if len(result) else None
33
+ return next(linter.run(), None)
34
+
35
+
36
+ class TestChecker(unittest.TestCase):
37
+ def tearDown(self):
38
+ Checker.attribute = "optional"
39
+ Checker.pattern = None
40
+
41
+ def test_author_optional(self):
42
+ self.assertIsNone(check("", attribute="optional"))
43
+ self.assertIsNone(check("Jon Parise", attribute="optional"))
44
+
45
+ def test_author_required(self):
46
+ self.assertIsNone(check("Jon Parise", attribute="required"))
47
+
48
+ def test_author_required_but_missing(self):
49
+ lineno, offset, message, _ = check("", attribute="required")
50
+ self.assertEqual(lineno, 0)
51
+ self.assertEqual(offset, 0)
52
+ self.assertTrue(message.startswith("A400"))
53
+
54
+ def test_author_forbidden(self):
55
+ self.assertIsNone(check("", attribute="forbidden"))
56
+
57
+ def test_author_forbidden_but_present(self):
58
+ lineno, offset, message, _ = check("Jon Parise", attribute="forbidden")
59
+ self.assertEqual(lineno, 1)
60
+ self.assertEqual(offset, 0)
61
+ self.assertTrue(message.startswith("A401"))
62
+
63
+ def test_author_invalid_attribute(self):
64
+ with self.assertRaises(ValueError):
65
+ check("Jon Parise", attribute="invalid")
66
+
67
+ def test_author_pattern(self):
68
+ author = "Jon Parise <jon@example.com>"
69
+ self.assertIsNone(check(author))
70
+ self.assertIsNone(check(author, pattern=r""))
71
+ self.assertIsNone(check(author, pattern=r".*"))
72
+
73
+ def test_author_pattern_not_matched(self):
74
+ author = "Jon Parise <jon@example.com>"
75
+ lineno, offset, message, _ = check(author, pattern=r"^[\w\s]+$")
76
+ self.assertEqual(lineno, 1)
77
+ self.assertEqual(offset, 0)
78
+ self.assertTrue(message.startswith("A402"))
79
+
80
+ def test_multi_author_list_pattern(self):
81
+ author = [
82
+ "Jon Parise <jon@example.com>",
83
+ "Jesse Boswell <jesse@example.com>",
84
+ ]
85
+ self.assertIsNone(check(author))
86
+ self.assertIsNone(check(author, pattern=r""))
87
+ self.assertIsNone(check(author, pattern=r".*"))
88
+
89
+ def test_multi_author_list_pattern_partial_match(self):
90
+ author = [
91
+ "Jon Parise <jon@example.com>",
92
+ "Jesse Boswell <jesse@badexample.com>",
93
+ ]
94
+ results = check(author, pattern=r"^.+?@example.com>$")
95
+ self.assertIsNotNone(results)
96
+ self.assertEqual(len(results), 1)
97
+ for result in results:
98
+ lineno, offset, message, _ = result
99
+ self.assertEqual(lineno, 1)
100
+ self.assertEqual(offset, 0)
101
+ self.assertTrue(message.startswith("A402"))
102
+
103
+ def test_multi_author_list_pattern_not_matched(self):
104
+ author = [
105
+ "Jon Parise <jon@example.com>",
106
+ "Jesse Boswell <jesse@example.com>",
107
+ ]
108
+ results = check(author, pattern=r"^[\w\s]+$")
109
+ self.assertIsNotNone(results)
110
+ self.assertEqual(len(results), 2)
111
+ for result in results:
112
+ lineno, offset, message, _ = result
113
+ self.assertEqual(lineno, 1)
114
+ self.assertEqual(offset, 0)
115
+ self.assertTrue(message.startswith("A402"))
116
+
117
+ def test_multi_author_tuple_pattern(self):
118
+ author = (
119
+ "Jon Parise <jon@example.com>",
120
+ "Jesse Boswell <jesse@example.com>",
121
+ )
122
+ self.assertIsNone(check(author))
123
+ self.assertIsNone(check(author, pattern=r""))
124
+ self.assertIsNone(check(author, pattern=r".*"))
125
+
126
+ def test_multi_author_tuple_pattern_partial_match(self):
127
+ author = (
128
+ "Jon Parise <jon@example.com>",
129
+ "Jesse Boswell <jesse@badexample.com>",
130
+ )
131
+ results = check(author, pattern=r"^.+?@example.com>$")
132
+ self.assertIsNotNone(results)
133
+ self.assertEqual(len(results), 1)
134
+ for result in results:
135
+ lineno, offset, message, _ = result
136
+ self.assertEqual(lineno, 1)
137
+ self.assertEqual(offset, 0)
138
+ self.assertTrue(message.startswith("A402"))
139
+
140
+ def test_multi_author_tuple_pattern_not_matched(self):
141
+ author = (
142
+ "Jon Parise <jon@example.com>",
143
+ "Jesse Boswell <jesse@example.com>",
144
+ )
145
+ results = check(author, pattern=r"^[\w\s]+$")
146
+ self.assertIsNotNone(results)
147
+ self.assertEqual(len(results), 2)
148
+ for result in results:
149
+ lineno, offset, message, _ = result
150
+ self.assertEqual(lineno, 1)
151
+ self.assertEqual(offset, 0)
152
+ self.assertTrue(message.startswith("A402"))
153
+
154
+ def test_multi_author_set_pattern(self):
155
+ author = {
156
+ "Jon Parise <jon@example.com>",
157
+ "Jesse Boswell <jesse@example.com>",
158
+ }
159
+ self.assertIsNone(check(author))
160
+ self.assertIsNone(check(author, pattern=r""))
161
+ self.assertIsNone(check(author, pattern=r".*"))
162
+
163
+ def test_multi_author_set_pattern_partial_match(self):
164
+ author = {
165
+ "Jon Parise <jon@example.com>",
166
+ "Jesse Boswell <jesse@badexample.com>",
167
+ }
168
+ results = check(author, pattern=r"^.+?@example.com>$")
169
+ self.assertIsNotNone(results)
170
+ self.assertEqual(len(results), 1)
171
+ for result in results:
172
+ lineno, offset, message, _ = result
173
+ self.assertEqual(lineno, 1)
174
+ self.assertEqual(offset, 0)
175
+ self.assertTrue(message.startswith("A402"))
176
+
177
+ def test_multi_author_set_pattern_not_matched(self):
178
+ author = {
179
+ "Jon Parise <jon@example.com>",
180
+ "Jesse Boswell <jesse@example.com>",
181
+ }
182
+ results = check(author, pattern=r"^[\w\s]+$")
183
+ self.assertIsNotNone(results)
184
+ self.assertEqual(len(results), 2)
185
+ for result in results:
186
+ lineno, offset, message, _ = result
187
+ self.assertEqual(lineno, 1)
188
+ self.assertEqual(offset, 0)
189
+ self.assertTrue(message.startswith("A402"))
190
+
191
+ def test_author_pattern_invalid_regex(self):
192
+ with self.assertRaises(ValueError):
193
+ check("Jon Parise", pattern=r"\[[")
194
+
195
+
196
+ if __name__ == "__main__":
197
+ unittest.main()
@@ -1,59 +0,0 @@
1
- Changes
2
- =======
3
-
4
- 2.0.0 (2021-12-30)
5
- ------------------
6
-
7
- * Drop support for Python 2.7 and 3.6, and add 3.10.
8
- * Drop support for flake8 2.x versions.
9
-
10
- 1.2.0 (2020-10-12)
11
- ------------------
12
-
13
- * Drop Python 3.5 support and add versions 3.8 and 3.9.
14
- * Minor documentation improvements.
15
-
16
- 1.1.4 (2017-07-29)
17
- ------------------
18
-
19
- * Minor code and packaging cleanup.
20
-
21
- 1.1.3 (2017-07-28)
22
- ------------------
23
-
24
- * Drop the ``setup_requires`` dependency on flake8.
25
-
26
- 1.1.2 (2017-01-12)
27
- ------------------
28
-
29
- * Only apply the author regular expression pattern check when a pattern has
30
- actually been configured.
31
-
32
- 1.1.1 (2016-11-06)
33
- ------------------
34
-
35
- * Fix configuration file parsing for flake8 3.0+.
36
-
37
- 1.1.0 (2016-08-15)
38
- ------------------
39
-
40
- * Support flake8 3.0.0 (required for development)
41
-
42
-
43
- 1.0.2 (2016-06-02)
44
- ------------------
45
-
46
- * Support flake8 version 2.6.0
47
- * Drop test dependency on pytest (development only)
48
-
49
-
50
- 1.0.1 (2016-05-11)
51
- ------------------
52
-
53
- * Fixed parser validation for configuration-based options
54
-
55
-
56
- 1.0.0 (2016-04-30)
57
- ------------------
58
-
59
- * Initial release
@@ -1,6 +0,0 @@
1
- include CHANGES.rst
2
- include LICENSE
3
- include README.rst
4
-
5
- recursive-include tests *
6
- recursive-exclude * *.py[co]
@@ -1,16 +0,0 @@
1
- CHANGES.rst
2
- LICENSE
3
- MANIFEST.in
4
- README.rst
5
- flake8_author.py
6
- setup.cfg
7
- setup.py
8
- flake8_author.egg-info/PKG-INFO
9
- flake8_author.egg-info/SOURCES.txt
10
- flake8_author.egg-info/dependency_links.txt
11
- flake8_author.egg-info/entry_points.txt
12
- flake8_author.egg-info/requires.txt
13
- flake8_author.egg-info/top_level.txt
14
- flake8_author.egg-info/zip-safe
15
- tests/__init__.py
16
- tests/test_checker.py
@@ -1,90 +0,0 @@
1
- """Flake8 extension that checks Python modules for an __author__ attribute.
2
-
3
- This extension can be configured to explicitly require or forbid __author__
4
- attributes. By default, __author__ is optional.
5
-
6
- If the __author__ attribute is allowed, its value will be validated against a
7
- configurable regular expression pattern (defaults to '.*').
8
- """
9
-
10
- import ast
11
- import re
12
-
13
- __author__ = 'Jon Parise'
14
- __version__ = '2.0.0'
15
-
16
-
17
- class Checker(object):
18
- """flake8 __author__ checker"""
19
-
20
- name = 'author'
21
- options = {}
22
- version = __version__
23
-
24
- # Attribute existence choices
25
- attribute_choices = ('optional', 'required', 'forbidden')
26
-
27
- def __init__(self, tree, filename):
28
- self.tree = tree
29
-
30
- @classmethod
31
- def add_options(cls, parser):
32
- extra_kwargs = {'parse_from_config': True}
33
-
34
- parser.add_option(
35
- '--author-attribute',
36
- default='optional',
37
- help="__author__ attribute: {0}".format(
38
- ', '.join(cls.attribute_choices)),
39
- **extra_kwargs)
40
- parser.add_option(
41
- '--author-pattern',
42
- default=r'',
43
- help="__author__ attribute validation pattern (regex)",
44
- **extra_kwargs)
45
-
46
- @classmethod
47
- def parse_options(cls, options):
48
- if options.author_attribute in cls.attribute_choices:
49
- cls.options['attribute'] = options.author_attribute
50
- else:
51
- raise ValueError(
52
- "author-attribute: '{0}' must be one of: {1}".format(
53
- options.author_attribute,
54
- ', '.join(cls.attribute_choices)))
55
-
56
- # Only build a regular expression object when we've been configured
57
- # with a pattern that doesn't match all strings. This naively just
58
- # checks for the default empty pattern string as well as `.*`.
59
- if options.author_pattern and options.author_pattern != '.*':
60
- try:
61
- cls.options['pattern'] = re.compile(options.author_pattern)
62
- except re.error as e:
63
- raise ValueError("author-pattern: '{0}': {1}".format(
64
- options.author_pattern, e))
65
-
66
- def find_author_node(self, tree):
67
- for node in tree.body:
68
- if not isinstance(node, ast.Assign):
69
- continue
70
-
71
- for target in node.targets:
72
- if isinstance(target, ast.Name) and target.id == '__author__':
73
- return node
74
-
75
- def run(self):
76
- node = self.find_author_node(self.tree)
77
-
78
- if node is None and self.options['attribute'] == 'required':
79
- message = 'A400 module-level __author__ attribute required'
80
- yield 0, 0, message, type(self)
81
-
82
- elif node and self.options['attribute'] == 'forbidden':
83
- message = 'A401 __author__ attributes are not allowed'
84
- yield node.lineno, node.col_offset, message, type(self)
85
-
86
- elif (node and 'pattern' in self.options and
87
- not self.options['pattern'].match(node.value.s)):
88
- message = ('A402 __author__ value "{0}" does not match "{1}"'
89
- .format(node.value.s, self.options['pattern'].pattern))
90
- yield node.lineno, node.col_offset, message, type(self)
@@ -1,44 +0,0 @@
1
- [metadata]
2
- name = flake8-author
3
- version = attr: flake8_author.__version__
4
- author = Jon Parise
5
- author_email = jon@indelible.org
6
- description = Flake8 __author__ checker
7
- long_description = file: README.rst, LICENSE, CHANGES.rst
8
- license = MIT
9
- url = https://github.com/jparise/flake8-author
10
- project_urls =
11
- Source Code = https://github.com/jparise/flake8-author
12
- Issue Tracker = https://github.com/jparise/flake8-author/issues
13
- keywords = flake8 __author__
14
- classifiers =
15
- Development Status :: 5 - Production/Stable
16
- Environment :: Console
17
- Framework :: Flake8
18
- Intended Audience :: Developers
19
- License :: OSI Approved :: MIT License
20
- Programming Language :: Python
21
- Programming Language :: Python :: 3 :: Only
22
- Programming Language :: Python :: 3.7
23
- Programming Language :: Python :: 3.8
24
- Programming Language :: Python :: 3.9
25
- Programming Language :: Python :: 3.10
26
- Topic :: Software Development :: Libraries :: Python Modules
27
- Topic :: Software Development :: Quality Assurance
28
-
29
- [options]
30
- py_modules = flake8_author
31
- python_requires = >= 3.7
32
- zip_safe = True
33
-
34
- [options.entry_points]
35
- flake8.extension =
36
- A40 = flake8_author:Checker
37
-
38
- [flake8]
39
- exclude = .eggs/,.git/,.venv/,build/,dist/
40
-
41
- [egg_info]
42
- tag_build =
43
- tag_date = 0
44
-
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- from setuptools import setup
4
-
5
- setup(
6
- name="flake8-author",
7
- install_requires=["flake8>=3.0.0"],
8
- tests_require=['flake8>=3.0.0'],
9
- test_suite="tests",
10
- )
File without changes
@@ -1,76 +0,0 @@
1
- import ast
2
- import unittest
3
-
4
- from flake8.main.application import Application
5
- from flake8_author import Checker
6
-
7
-
8
- def make_linter(code, path='example.py', argv=None):
9
- app = Application()
10
- app.initialize(argv)
11
- Checker.parse_options(app.options)
12
- tree = ast.parse(code, path)
13
- return Checker(tree, path)
14
-
15
-
16
- def check(author, attribute=None, pattern=None):
17
- code = '__author__ = "{0}"'.format(author) if author else ''
18
-
19
- argv = []
20
- if attribute:
21
- argv.append('--author-attribute={0}'.format(attribute))
22
- if pattern:
23
- argv.append('--author-pattern={0}'.format(pattern))
24
-
25
- linter = make_linter(code, argv=argv)
26
- return next(linter.run(), None)
27
-
28
-
29
- class TestChecker(unittest.TestCase):
30
-
31
- def test_author_optional(self):
32
- self.assertIsNone(check('', attribute='optional'))
33
- self.assertIsNone(check('Jon Parise', attribute='optional'))
34
-
35
- def test_author_required(self):
36
- self.assertIsNone(check('Jon Parise', attribute='required'))
37
-
38
- def test_author_required_but_missing(self):
39
- lineno, offset, message, _ = check('', attribute='required')
40
- self.assertEqual(lineno, 0)
41
- self.assertEqual(offset, 0)
42
- self.assertTrue(message.startswith('A400'))
43
-
44
- def test_author_forbidden(self):
45
- self.assertIsNone(check('', attribute='forbidden'))
46
-
47
- def test_author_forbidden_but_present(self):
48
- lineno, offset, message, _ = check('Jon Parise', attribute='forbidden')
49
- self.assertEqual(lineno, 1)
50
- self.assertEqual(offset, 0)
51
- self.assertTrue(message.startswith('A401'))
52
-
53
- def test_author_invalid_attribute(self):
54
- with self.assertRaises(ValueError):
55
- check('Jon Parise', attribute='invalid')
56
-
57
- def test_author_pattern(self):
58
- author = 'Jon Parise <jon@example.com>'
59
- self.assertIsNone(check(author))
60
- self.assertIsNone(check(author, pattern=r''))
61
- self.assertIsNone(check(author, pattern=r'.*'))
62
-
63
- def test_author_pattern_not_matched(self):
64
- author = 'Jon Parise <jon@example.com>'
65
- lineno, offset, message, _ = check(author, pattern=r'^[\w\s]+$')
66
- self.assertEqual(lineno, 1)
67
- self.assertEqual(offset, 0)
68
- self.assertTrue(message.startswith('A402'))
69
-
70
- def test_author_pattern_invalid_regex(self):
71
- with self.assertRaises(ValueError):
72
- check('Jon Parise', pattern=r'\[[')
73
-
74
-
75
- if __name__ == '__main__':
76
- unittest.main()
File without changes