mirror of
https://github.com/python/cpython
synced 2024-10-14 19:21:29 +00:00
gh-104050: Run mypy on clinic.py
in CI (#104421)
* Add basic mypy workflow to CI * Make the type check pass --------- Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com> Co-authored-by: Nikita Sobolev <mail@sobolevn.me> Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
This commit is contained in:
parent
a6bcc8fb92
commit
9d41f83c58
7
.github/dependabot.yml
vendored
7
.github/dependabot.yml
vendored
|
@ -12,3 +12,10 @@ updates:
|
|||
update-types:
|
||||
- "version-update:semver-minor"
|
||||
- "version-update:semver-patch"
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/Tools/clinic/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
labels:
|
||||
- "skip issue"
|
||||
- "skip news"
|
||||
|
|
39
.github/workflows/mypy.yml
vendored
Normal file
39
.github/workflows/mypy.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Workflow to run mypy on select parts of the CPython repo
|
||||
name: mypy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths:
|
||||
- "Tools/clinic/**"
|
||||
- ".github/workflows/mypy.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||
FORCE_COLOR: 1
|
||||
TERM: xterm-256color # needed for FORCE_COLOR to work on mypy on Ubuntu, see https://github.com/python/mypy/issues/13817
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
mypy:
|
||||
name: Run mypy on Tools/clinic/
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
cache: pip
|
||||
cache-dependency-path: Tools/clinic/requirements-dev.txt
|
||||
- run: pip install -r Tools/clinic/requirements-dev.txt
|
||||
- run: mypy --config-file Tools/clinic/mypy.ini
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import abc
|
||||
import ast
|
||||
import builtins as bltns
|
||||
import collections
|
||||
import contextlib
|
||||
import copy
|
||||
|
@ -26,7 +27,9 @@
|
|||
import traceback
|
||||
import types
|
||||
|
||||
from collections.abc import Callable
|
||||
from types import *
|
||||
from typing import Any, NamedTuple
|
||||
|
||||
# TODO:
|
||||
#
|
||||
|
@ -78,8 +81,13 @@ def __repr__(self):
|
|||
|
||||
sig_end_marker = '--'
|
||||
|
||||
Appender = Callable[[str], None]
|
||||
Outputter = Callable[[None], str]
|
||||
|
||||
_text_accumulator_nt = collections.namedtuple("_text_accumulator", "text append output")
|
||||
class _TextAccumulator(NamedTuple):
|
||||
text: list[str]
|
||||
append: Appender
|
||||
output: Outputter
|
||||
|
||||
def _text_accumulator():
|
||||
text = []
|
||||
|
@ -87,10 +95,12 @@ def output():
|
|||
s = ''.join(text)
|
||||
text.clear()
|
||||
return s
|
||||
return _text_accumulator_nt(text, text.append, output)
|
||||
return _TextAccumulator(text, text.append, output)
|
||||
|
||||
|
||||
text_accumulator_nt = collections.namedtuple("text_accumulator", "text append")
|
||||
class TextAccumulator(NamedTuple):
|
||||
text: list[str]
|
||||
append: Appender
|
||||
|
||||
def text_accumulator():
|
||||
"""
|
||||
|
@ -104,7 +114,7 @@ def text_accumulator():
|
|||
empties the accumulator.
|
||||
"""
|
||||
text, append, output = _text_accumulator()
|
||||
return text_accumulator_nt(append, output)
|
||||
return TextAccumulator(append, output)
|
||||
|
||||
|
||||
def warn_or_fail(fail=False, *args, filename=None, line_number=None):
|
||||
|
@ -1925,8 +1935,10 @@ def dump(self):
|
|||
# maps strings to Language objects.
|
||||
# "languages" maps the name of the language ("C", "Python").
|
||||
# "extensions" maps the file extension ("c", "py").
|
||||
LangDict = dict[str, Callable[[str], Language]]
|
||||
|
||||
languages = { 'C': CLanguage, 'Python': PythonLanguage }
|
||||
extensions = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx".split() }
|
||||
extensions: LangDict = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx".split() }
|
||||
extensions['py'] = PythonLanguage
|
||||
|
||||
|
||||
|
@ -2558,15 +2570,15 @@ class CConverter(metaclass=CConverterAutoRegister):
|
|||
"""
|
||||
|
||||
# The C name to use for this variable.
|
||||
name = None
|
||||
name: str | None = None
|
||||
|
||||
# The Python name to use for this variable.
|
||||
py_name = None
|
||||
py_name: str | None = None
|
||||
|
||||
# The C type to use for this variable.
|
||||
# 'type' should be a Python string specifying the type, e.g. "int".
|
||||
# If this is a pointer type, the type string should end with ' *'.
|
||||
type = None
|
||||
type: str | None = None
|
||||
|
||||
# The Python default value for this parameter, as a Python value.
|
||||
# Or the magic value "unspecified" if there is no default.
|
||||
|
@ -2577,15 +2589,15 @@ class CConverter(metaclass=CConverterAutoRegister):
|
|||
|
||||
# If not None, default must be isinstance() of this type.
|
||||
# (You can also specify a tuple of types.)
|
||||
default_type = None
|
||||
default_type: bltns.type[Any] | tuple[bltns.type[Any], ...] | None = None
|
||||
|
||||
# "default" converted into a C value, as a string.
|
||||
# Or None if there is no default.
|
||||
c_default = None
|
||||
c_default: str | None = None
|
||||
|
||||
# "default" converted into a Python value, as a string.
|
||||
# Or None if there is no default.
|
||||
py_default = None
|
||||
py_default: str | None = None
|
||||
|
||||
# The default value used to initialize the C variable when
|
||||
# there is no default, but not specifying a default may
|
||||
|
@ -2597,14 +2609,14 @@ class CConverter(metaclass=CConverterAutoRegister):
|
|||
#
|
||||
# This value is specified as a string.
|
||||
# Every non-abstract subclass should supply a valid value.
|
||||
c_ignored_default = 'NULL'
|
||||
c_ignored_default: str = 'NULL'
|
||||
|
||||
# If true, wrap with Py_UNUSED.
|
||||
unused = False
|
||||
|
||||
# The C converter *function* to be used, if any.
|
||||
# (If this is not None, format_unit must be 'O&'.)
|
||||
converter = None
|
||||
converter: str | None = None
|
||||
|
||||
# Should Argument Clinic add a '&' before the name of
|
||||
# the variable when passing it into the _impl function?
|
||||
|
@ -3432,7 +3444,7 @@ class robuffer: pass
|
|||
def str_converter_key(types, encoding, zeroes):
|
||||
return (frozenset(types), bool(encoding), bool(zeroes))
|
||||
|
||||
str_converter_argument_map = {}
|
||||
str_converter_argument_map: dict[str, str] = {}
|
||||
|
||||
class str_converter(CConverter):
|
||||
type = 'const char *'
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import re
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
|
||||
def negate(condition):
|
||||
|
||||
TokenAndCondition = tuple[str, str]
|
||||
TokenStack = list[TokenAndCondition]
|
||||
|
||||
def negate(condition: str) -> str:
|
||||
"""
|
||||
Returns a CPP conditional that is the opposite of the conditional passed in.
|
||||
"""
|
||||
|
@ -22,17 +27,18 @@ class Monitor:
|
|||
Anyway this implementation seems to work well enough for the CPython sources.
|
||||
"""
|
||||
|
||||
is_a_simple_defined: Callable[[str], re.Match[str] | None]
|
||||
is_a_simple_defined = re.compile(r'^defined\s*\(\s*[A-Za-z0-9_]+\s*\)$').match
|
||||
|
||||
def __init__(self, filename=None, *, verbose=False):
|
||||
self.stack = []
|
||||
def __init__(self, filename=None, *, verbose: bool = False):
|
||||
self.stack: TokenStack = []
|
||||
self.in_comment = False
|
||||
self.continuation = None
|
||||
self.continuation: str | None = None
|
||||
self.line_number = 0
|
||||
self.filename = filename
|
||||
self.verbose = verbose
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return ''.join((
|
||||
'<Monitor ',
|
||||
str(id(self)),
|
||||
|
@ -40,10 +46,10 @@ def __repr__(self):
|
|||
" condition=", repr(self.condition()),
|
||||
">"))
|
||||
|
||||
def status(self):
|
||||
def status(self) -> str:
|
||||
return str(self.line_number).rjust(4) + ": " + self.condition()
|
||||
|
||||
def condition(self):
|
||||
def condition(self) -> str:
|
||||
"""
|
||||
Returns the current preprocessor state, as a single #if condition.
|
||||
"""
|
||||
|
@ -62,15 +68,15 @@ def close(self):
|
|||
if self.stack:
|
||||
self.fail("Ended file while still in a preprocessor conditional block!")
|
||||
|
||||
def write(self, s):
|
||||
def write(self, s: str) -> None:
|
||||
for line in s.split("\n"):
|
||||
self.writeline(line)
|
||||
|
||||
def writeline(self, line):
|
||||
def writeline(self, line: str) -> None:
|
||||
self.line_number += 1
|
||||
line = line.strip()
|
||||
|
||||
def pop_stack():
|
||||
def pop_stack() -> TokenAndCondition:
|
||||
if not self.stack:
|
||||
self.fail("#" + token + " without matching #if / #ifdef / #ifndef!")
|
||||
return self.stack.pop()
|
||||
|
|
11
Tools/clinic/mypy.ini
Normal file
11
Tools/clinic/mypy.ini
Normal file
|
@ -0,0 +1,11 @@
|
|||
[mypy]
|
||||
# make sure clinic can still be run on Python 3.10
|
||||
python_version = 3.10
|
||||
pretty = True
|
||||
enable_error_code = ignore-without-code
|
||||
disallow_any_generics = True
|
||||
strict_concatenate = True
|
||||
warn_redundant_casts = True
|
||||
warn_unused_ignores = True
|
||||
warn_unused_configs = True
|
||||
files = Tools/clinic/
|
2
Tools/clinic/requirements-dev.txt
Normal file
2
Tools/clinic/requirements-dev.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Requirements file for external linters and checks we run on Tools/clinic/ in CI
|
||||
mypy==1.2.0
|
Loading…
Reference in a new issue