diff --git a/mypy.ini b/mypy.ini index 83953337..4735da0b 100644 --- a/mypy.ini +++ b/mypy.ini @@ -17,7 +17,7 @@ txt_report = mypy ; TODO: Adopt --strict settings, iterating towards something like: ; https://github.com/pypa/packaging/blob/master/setup.cfg ; Starting with modules that have annotations applied via MonkeyType -[mypy-twine.auth,twine.cli,twine.exceptions,twine.package,twine.repository,twine.utils,twine.commands,twine.wheel,twine.wininst,twine.commands.register] +[mypy-twine.auth,twine.cli,twine.exceptions,twine.package,twine.repository,twine.utils,twine.wheel,twine.wininst,twine.commands] ; Enabling this will fail on subclasses of untype imports, e.g. tqdm ; disallow_subclassing_any = True disallow_any_generics = True diff --git a/tests/test_check.py b/tests/test_check.py index ef69fd86..0a0a0f4b 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -48,7 +48,7 @@ def test_check_no_distributions(monkeypatch): monkeypatch.setattr(commands, "_find_dists", lambda a: []) - assert not check.check("dist/*", output_stream=stream) + assert not check.check(["dist/*"], output_stream=stream) assert stream.getvalue() == "No files to check.\n" @@ -72,7 +72,7 @@ def test_check_passing_distribution(monkeypatch): ) monkeypatch.setattr(check, "_WarningStream", lambda: warning_stream) - assert not check.check("dist/*", output_stream=output_stream) + assert not check.check(["dist/*"], output_stream=output_stream) assert output_stream.getvalue() == "Checking dist/dist.tar.gz: PASSED\n" assert renderer.render.calls == [pretend.call("blah", stream=warning_stream)] @@ -94,7 +94,7 @@ def test_check_no_description(monkeypatch, capsys): # used to crash with `AttributeError` output_stream = io.StringIO() - check.check("dist/*", output_stream=output_stream) + check.check(["dist/*"], output_stream=output_stream) assert output_stream.getvalue() == ( "Checking dist/dist.tar.gz: PASSED, with warnings\n" " warning: `long_description_content_type` missing. " @@ -123,7 +123,7 @@ def test_check_failing_distribution(monkeypatch): ) monkeypatch.setattr(check, "_WarningStream", lambda: warning_stream) - assert check.check("dist/*", output_stream=output_stream) + assert check.check(["dist/*"], output_stream=output_stream) assert output_stream.getvalue() == ( "Checking dist/dist.tar.gz: FAILED\n" " `long_description` has syntax errors in markup and would not be " diff --git a/twine/commands/check.py b/twine/commands/check.py index d444635d..1ecac73b 100644 --- a/twine/commands/check.py +++ b/twine/commands/check.py @@ -16,6 +16,11 @@ import io import re import sys +import textwrap +from typing import IO +from typing import List +from typing import Tuple +from typing import cast import readme_renderer.rst @@ -43,10 +48,10 @@ class _WarningStream: - def __init__(self): + def __init__(self) -> None: self.output = io.StringIO() - def write(self, text): + def write(self, text: str) -> None: matched = _REPORT_RE.search(text) if not matched: @@ -61,11 +66,13 @@ def write(self, text): ) ) - def __str__(self): + def __str__(self) -> str: return self.output.getvalue() -def _check_file(filename, render_warning_stream): +def _check_file( + filename: str, render_warning_stream: _WarningStream +) -> Tuple[List[str], bool]: """Check given distribution.""" warnings = [] is_ok = True @@ -73,8 +80,8 @@ def _check_file(filename, render_warning_stream): package = package_file.PackageFile.from_filename(filename, comment=None) metadata = package.metadata_dictionary() - description = metadata["description"] - description_content_type = metadata["description_content_type"] + description = cast(str, metadata["description"]) + description_content_type = cast(str, metadata["description_content_type"]) if description_content_type is None: warnings.append( @@ -97,18 +104,7 @@ def _check_file(filename, render_warning_stream): return warnings, is_ok -# TODO: Replace with textwrap.indent when Python 2 support is dropped -def _indented(text, prefix): - """Adds 'prefix' to all non-empty lines on 'text'.""" - - def prefixed_lines(): - for line in text.splitlines(True): - yield (prefix + line if line.strip() else line) - - return "".join(prefixed_lines()) - - -def check(dists, output_stream=sys.stdout): +def check(dists: List[str], output_stream: IO[str] = sys.stdout) -> bool: uploads = [i for i in commands._find_dists(dists) if not i.endswith(".asc")] if not uploads: # Return early, if there are no files to check. output_stream.write("No files to check.\n") @@ -130,8 +126,8 @@ def check(dists, output_stream=sys.stdout): "`long_description` has syntax errors in markup and " "would not be rendered on PyPI.\n" ) - output_stream.write(_indented(error_text, " ")) - output_stream.write(_indented(str(render_warning_stream), " ")) + output_stream.write(textwrap.indent(error_text, " ")) + output_stream.write(textwrap.indent(str(render_warning_stream), " ")) elif warnings: output_stream.write("PASSED, with warnings\n") else: @@ -144,7 +140,7 @@ def check(dists, output_stream=sys.stdout): return failure -def main(args): +def main(args: List[str]) -> bool: parser = argparse.ArgumentParser(prog="twine check") parser.add_argument( "dists", @@ -153,7 +149,7 @@ def main(args): help="The distribution files to check, usually dist/*", ) - args = parser.parse_args(args) + parsed_args = parser.parse_args(args) # Call the check function with the arguments from the command line - return check(args.dists) + return check(parsed_args.dists) diff --git a/twine/package.py b/twine/package.py index abfb0c64..892141fa 100644 --- a/twine/package.py +++ b/twine/package.py @@ -16,7 +16,6 @@ import io import os import subprocess -from typing import IO from typing import Dict from typing import Optional from typing import Sequence @@ -46,7 +45,7 @@ ".zip": "sdist", } -MetadataValue = Union[str, Sequence[str], Tuple[str, IO, str]] +MetadataValue = Union[str, Sequence[str]] class PackageFile: diff --git a/twine/repository.py b/twine/repository.py index 8a82baa4..08eb0cb0 100644 --- a/twine/repository.py +++ b/twine/repository.py @@ -103,9 +103,7 @@ def close(self) -> None: self.session.close() @staticmethod - def _convert_data_to_list_of_tuples( - data: Dict[str, package_file.MetadataValue] - ) -> List[Tuple[str, package_file.MetadataValue]]: + def _convert_data_to_list_of_tuples(data: Dict[str, Any]) -> List[Tuple[str, Any]]: data_to_send = [] for key, value in data.items(): if key in KEYWORDS_TO_NOT_FLATTEN or not isinstance(value, (list, tuple)):