Skip to content

Commit b4403a1

Browse files
authored
Start checking using Flake8-PYI (#4380)
1 parent a001ec8 commit b4403a1

File tree

3 files changed

+79
-90
lines changed

3 files changed

+79
-90
lines changed

ruff.toml

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
exclude = [
2+
"**/_vendor",
3+
"setuptools/_distutils",
4+
"setuptools/config/_validate_pyproject",
5+
]
6+
17
[lint]
28
extend-select = [
39
"C901",
@@ -6,6 +12,7 @@ extend-select = [
612
# local
713
"FA", # flake8-future-annotations
814
"F404", # late-future-import
15+
"PYI", # flake8-pyi
916
"UP", # pyupgrade
1017
"YTT", # flake8-2020
1118
]
@@ -32,23 +39,13 @@ ignore = [
3239
"ISC001",
3340
"ISC002",
3441
]
35-
exclude = [
36-
"**/_vendor",
37-
"setuptools/_distutils",
38-
"setuptools/config/_validate_pyproject",
39-
]
4042

4143
[lint.per-file-ignores]
4244
# Auto-generated code
4345
"setuptools/config/_validate_pyproject/*" = ["FA100"]
4446

4547
[format]
46-
exclude = [
47-
"**/_vendor",
48-
"setuptools/_distutils",
49-
"setuptools/config/_validate_pyproject",
50-
]
51-
# Enable preview, required for quote-style = "preserve"
48+
# Enable preview to get hugged parenthesis unwrapping
5249
preview = true
5350
# https://docs.astral.sh/ruff/settings/#format-quote-style
5451
quote-style = "preserve"

setuptools/depends.py

Lines changed: 64 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .extern.packaging.version import Version
1010

1111

12-
__all__ = ['Require', 'find_module', 'get_module_constant', 'extract_constant']
12+
__all__ = ['Require', 'find_module']
1313

1414

1515
class Require:
@@ -95,86 +95,73 @@ def empty():
9595
return contextlib.closing(f)
9696

9797

98-
def get_module_constant(module, symbol, default=-1, paths=None):
99-
"""Find 'module' by searching 'paths', and extract 'symbol'
100-
101-
Return 'None' if 'module' does not exist on 'paths', or it does not define
102-
'symbol'. If the module defines 'symbol' as a constant, return the
103-
constant. Otherwise, return 'default'."""
104-
105-
try:
106-
f, path, (suffix, mode, kind) = info = find_module(module, paths)
107-
except ImportError:
108-
# Module doesn't exist
109-
return None
110-
111-
with maybe_close(f):
112-
if kind == PY_COMPILED:
113-
f.read(8) # skip magic & date
114-
code = marshal.load(f)
115-
elif kind == PY_FROZEN:
116-
code = _imp.get_frozen_object(module, paths)
117-
elif kind == PY_SOURCE:
118-
code = compile(f.read(), path, 'exec')
119-
else:
120-
# Not something we can parse; we'll have to import it. :(
121-
imported = _imp.get_module(module, paths, info)
122-
return getattr(imported, symbol, None)
123-
124-
return extract_constant(code, symbol, default)
125-
126-
127-
def extract_constant(code, symbol, default=-1):
128-
"""Extract the constant value of 'symbol' from 'code'
129-
130-
If the name 'symbol' is bound to a constant value by the Python code
131-
object 'code', return that value. If 'symbol' is bound to an expression,
132-
return 'default'. Otherwise, return 'None'.
133-
134-
Return value is based on the first assignment to 'symbol'. 'symbol' must
135-
be a global, or at least a non-"fast" local in the code block. That is,
136-
only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
137-
must be present in 'code.co_names'.
138-
"""
139-
if symbol not in code.co_names:
140-
# name's not there, can't possibly be an assignment
141-
return None
142-
143-
name_idx = list(code.co_names).index(symbol)
144-
145-
STORE_NAME = dis.opmap['STORE_NAME']
146-
STORE_GLOBAL = dis.opmap['STORE_GLOBAL']
147-
LOAD_CONST = dis.opmap['LOAD_CONST']
148-
149-
const = default
150-
151-
for byte_code in dis.Bytecode(code):
152-
op = byte_code.opcode
153-
arg = byte_code.arg
154-
155-
if op == LOAD_CONST:
156-
const = code.co_consts[arg]
157-
elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL):
158-
return const
159-
else:
160-
const = default
98+
# Some objects are not available on some platforms.
99+
# XXX it'd be better to test assertions about bytecode instead.
100+
if not sys.platform.startswith('java') and sys.platform != 'cli':
101+
102+
def get_module_constant(module, symbol, default=-1, paths=None):
103+
"""Find 'module' by searching 'paths', and extract 'symbol'
104+
105+
Return 'None' if 'module' does not exist on 'paths', or it does not define
106+
'symbol'. If the module defines 'symbol' as a constant, return the
107+
constant. Otherwise, return 'default'."""
108+
109+
try:
110+
f, path, (suffix, mode, kind) = info = find_module(module, paths)
111+
except ImportError:
112+
# Module doesn't exist
113+
return None
114+
115+
with maybe_close(f):
116+
if kind == PY_COMPILED:
117+
f.read(8) # skip magic & date
118+
code = marshal.load(f)
119+
elif kind == PY_FROZEN:
120+
code = _imp.get_frozen_object(module, paths)
121+
elif kind == PY_SOURCE:
122+
code = compile(f.read(), path, 'exec')
123+
else:
124+
# Not something we can parse; we'll have to import it. :(
125+
imported = _imp.get_module(module, paths, info)
126+
return getattr(imported, symbol, None)
127+
128+
return extract_constant(code, symbol, default)
129+
130+
def extract_constant(code, symbol, default=-1):
131+
"""Extract the constant value of 'symbol' from 'code'
132+
133+
If the name 'symbol' is bound to a constant value by the Python code
134+
object 'code', return that value. If 'symbol' is bound to an expression,
135+
return 'default'. Otherwise, return 'None'.
136+
137+
Return value is based on the first assignment to 'symbol'. 'symbol' must
138+
be a global, or at least a non-"fast" local in the code block. That is,
139+
only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
140+
must be present in 'code.co_names'.
141+
"""
142+
if symbol not in code.co_names:
143+
# name's not there, can't possibly be an assignment
144+
return None
161145

162-
return None
146+
name_idx = list(code.co_names).index(symbol)
163147

148+
STORE_NAME = dis.opmap['STORE_NAME']
149+
STORE_GLOBAL = dis.opmap['STORE_GLOBAL']
150+
LOAD_CONST = dis.opmap['LOAD_CONST']
164151

165-
def _update_globals():
166-
"""
167-
Patch the globals to remove the objects not available on some platforms.
152+
const = default
168153

169-
XXX it'd be better to test assertions about bytecode instead.
170-
"""
154+
for byte_code in dis.Bytecode(code):
155+
op = byte_code.opcode
156+
arg = byte_code.arg
171157

172-
if not sys.platform.startswith('java') and sys.platform != 'cli':
173-
return
174-
incompatible = 'extract_constant', 'get_module_constant'
175-
for name in incompatible:
176-
del globals()[name]
177-
__all__.remove(name)
158+
if op == LOAD_CONST:
159+
const = code.co_consts[arg]
160+
elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL):
161+
return const
162+
else:
163+
const = default
178164

165+
return None
179166

180-
_update_globals()
167+
__all__ += ['get_module_constant', 'extract_constant']

setuptools/tests/test_easy_install.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
import itertools
1111
import distutils.errors
1212
import io
13+
from typing import NamedTuple
1314
import zipfile
1415
import time
1516
import re
1617
import subprocess
1718
import pathlib
1819
import warnings
19-
from collections import namedtuple
2020
from pathlib import Path
2121
from unittest import mock
2222

@@ -1344,7 +1344,12 @@ def test_header(self):
13441344
assert not hdr.startswith('\\"')
13451345

13461346

1347-
VersionStub = namedtuple("VersionStub", "major, minor, micro, releaselevel, serial")
1347+
class VersionStub(NamedTuple):
1348+
major: int
1349+
minor: int
1350+
micro: int
1351+
releaselevel: str
1352+
serial: int
13481353

13491354

13501355
def test_use_correct_python_version_string(tmpdir, tmpdir_cwd, monkeypatch):

0 commit comments

Comments
 (0)