Skip to content

Commit d03f28f

Browse files
committed
Enable typeguard during build time and tests, fix issues found
It is also possible to use typeguard in osc-wrapper.py by setting OSC_TYPEGUARD=1.
1 parent 706fcf5 commit d03f28f

11 files changed

Lines changed: 116 additions & 47 deletions

File tree

.github/workflows/tests.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ jobs:
135135
sudo apt-get -y --no-install-recommends install git python3-behave diffstat diffutils python3 python3-cryptography python3-pip python3-rpm python3-ruamel.yaml python3-setuptools python3-urllib3 obs-build obs-service-set-version
136136
# obs-scm-bridge is not available as a package at the moment, install it from github
137137
sudo pip3 config set global.break-system-packages 1
138+
sudo pip3 install typeguard
138139
sudo pip3 install git+https://github.com/openSUSE/obs-scm-bridge
139140
sudo chmod a+x /usr/local/lib/*/*/obs_scm_bridge
140141
sudo mkdir -p /usr/lib/obs/service
@@ -155,4 +156,4 @@ jobs:
155156
- name: "Run tests"
156157
run: |
157158
cd behave
158-
behave -Dosc=../osc-wrapper.py -Dgit-obs=../git-obs.py -Dgit-osc-precommit-hook=../git-osc-precommit-hook.py -Dpodman_max_containers=2
159+
OSC_TYPEGUARD=1 behave -Dosc=../osc-wrapper.py -Dgit-obs=../git-obs.py -Dgit-osc-precommit-hook=../git-osc-precommit-hook.py -Dpodman_max_containers=2

contrib/osc.spec

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@
3030
%bcond_with fdupes
3131
%endif
3232

33+
# use typeguard during build on distros where typeguard is available
34+
%if (0%{?suse_version} > 1500 || 0%{?fedora} >= 37)
35+
%bcond_without typeguard
36+
%else
37+
%bcond_with typeguard
38+
%endif
39+
3340
# the macro exists only on openSUSE based distros
3441
%if %{undefined python3_fix_shebang}
3542
%define python3_fix_shebang %nil
@@ -76,6 +83,9 @@ BuildRequires: %{use_python_pkg}-cryptography
7683
BuildRequires: %{use_python_pkg}-devel >= 3.6
7784
BuildRequires: %{use_python_pkg}-rpm
7885
BuildRequires: %{use_python_pkg}-setuptools
86+
%if %{with typeguard}
87+
BuildRequires: %{use_python_pkg}-typeguard
88+
%endif
7989
BuildRequires: %{use_python_pkg}-urllib3
8090
BuildRequires: %{ruamel_yaml_pkg}
8191
BuildRequires: diffstat

osc-wrapper.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,29 @@
44
This wrapper allows osc to be called from the source directory during development.
55
"""
66

7+
8+
import os
9+
10+
11+
USE_TYPEGUARD = os.environ.get("OSC_TYPEGUARD", "1").lower() in ("1", "true", "on")
12+
13+
if USE_TYPEGUARD:
14+
try:
15+
from typeguard import install_import_hook
16+
except ImportError:
17+
install_import_hook = None
18+
19+
if install_import_hook is None:
20+
try:
21+
from typeguard.importhook import install_import_hook
22+
except ImportError:
23+
install_import_hook = None
24+
25+
if install_import_hook:
26+
# install typeguard import hook only if available
27+
install_import_hook("osc")
28+
29+
730
import osc.babysitter
831

932
osc.babysitter.main()

osc/commandline.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4973,6 +4973,8 @@ def do_rdiff(self, subcmd, opts, *args):
49734973
rev2 = -rev - 1
49744974
else:
49754975
return
4976+
rev1 = str(rev1)
4977+
rev2 = str(rev2)
49764978
except:
49774979
print(f'Revision \'{opts.change}\' not an integer', file=sys.stderr)
49784980
return

osc/core.py

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,7 @@ def show_package_trigger_reason(apiurl: str, prj: str, pac: str, repo: str, arch
13331333
raise
13341334

13351335

1336-
def show_package_meta(apiurl: str, prj: str, pac: str, meta=False, blame=None):
1336+
def show_package_meta(apiurl: str, prj: str, pac: str, meta=False, blame=None) -> List[bytes]:
13371337
query: Dict[str, Union[str, int]] = {}
13381338
if meta:
13391339
query['meta'] = 1
@@ -2910,12 +2910,12 @@ def get_source_file_diff(dir, filename, rev, oldfilename=None, olddir=None, orig
29102910

29112911
def server_diff(
29122912
apiurl: str,
2913-
old_project: str,
2914-
old_package: str,
2915-
old_revision: str,
2913+
old_project: Optional[str],
2914+
old_package: Optional[str],
2915+
old_revision: Optional[str],
29162916
new_project: str,
29172917
new_package: str,
2918-
new_revision: str,
2918+
new_revision: Optional[str],
29192919
unified=False,
29202920
missingok=False,
29212921
meta=False,
@@ -2976,12 +2976,12 @@ def server_diff(
29762976

29772977
def server_diff_noex(
29782978
apiurl: str,
2979-
old_project: str,
2980-
old_package: str,
2981-
old_revision: str,
2979+
old_project: Optional[str],
2980+
old_package: Optional[str],
2981+
old_revision: Optional[str],
29822982
new_project: str,
29832983
new_package: str,
2984-
new_revision: str,
2984+
new_revision: Optional[str],
29852985
unified=False,
29862986
missingok=False,
29872987
meta=False,
@@ -3264,9 +3264,9 @@ def checkout_package(
32643264

32653265

32663266
def replace_pkg_meta(
3267-
pkgmeta, new_name: str, new_prj: str, keep_maintainers=False, dst_userid=None, keep_develproject=False,
3267+
pkgmeta: List[bytes], new_name: str, new_prj: str, keep_maintainers=False, dst_userid=None, keep_develproject=False,
32683268
keep_lock: bool = False, keep_scmsync: bool = True,
3269-
):
3269+
) -> str:
32703270
"""
32713271
update pkgmeta with new new_name and new_prj and set calling user as the
32723272
only maintainer (unless keep_maintainers is set). Additionally remove the
@@ -3509,7 +3509,7 @@ def aggregate_pac(
35093509

35103510
if meta_change:
35113511
src_meta = show_package_meta(apiurl, src_project, src_package_meta)
3512-
dst_meta = replace_pkg_meta(src_meta, dst_package_meta, dst_project)
3512+
dst_meta = replace_pkg_meta(src_meta, dst_package_meta, dst_project).split("\n")
35133513
meta_change = True
35143514

35153515
if disable_publish:
@@ -4815,25 +4815,26 @@ def get_commitlog(
48154815
# revision is srcmd5
48164816
revision_list = [i for i in revision_list if i.srcmd5 == revision]
48174817
else:
4818-
revision = int(revision)
4818+
assert revision is not None
4819+
revision_int = int(revision)
48194820
if revision_is_empty(revision_upper):
4820-
revision_list = [i for i in revision_list if i.rev == revision]
4821+
revision_list = [i for i in revision_list if i.rev == revision_int]
48214822
else:
4822-
revision_upper = int(revision_upper)
4823-
revision_list = [i for i in revision_list if i.rev <= revision_upper and i.rev >= revision]
4823+
revision_upper_int = int(revision_upper)
4824+
revision_list = [i for i in revision_list if i.rev <= revision_upper_int and i.rev >= revision_int]
48244825

48254826
if format == "csv":
48264827
f = io.StringIO()
48274828
writer = csv.writer(f, dialect="unix")
4828-
for revision in reversed(revision_list):
4829+
for i in reversed(revision_list):
48294830
writer.writerow(
48304831
(
4831-
revision.rev,
4832-
revision.user,
4833-
revision.get_time_str(),
4834-
revision.srcmd5,
4835-
revision.comment,
4836-
revision.requestid,
4832+
i.rev,
4833+
i.user,
4834+
i.get_time_str(),
4835+
i.srcmd5,
4836+
i.comment,
4837+
i.requestid,
48374838
)
48384839
)
48394840
f.seek(0)
@@ -4842,42 +4843,42 @@ def get_commitlog(
48424843

48434844
if format == "xml":
48444845
root = ET.Element("log")
4845-
for revision in reversed(revision_list):
4846+
for i in reversed(revision_list):
48464847
entry = ET.SubElement(root, "logentry")
4847-
entry.attrib["revision"] = str(revision.rev)
4848-
entry.attrib["srcmd5"] = revision.srcmd5
4849-
ET.SubElement(entry, "author").text = revision.user
4850-
ET.SubElement(entry, "date").text = revision.get_time_str()
4851-
ET.SubElement(entry, "requestid").text = str(revision.requestid) if revision.requestid else ""
4852-
ET.SubElement(entry, "msg").text = revision.comment or ""
4848+
entry.attrib["revision"] = str(i.rev)
4849+
entry.attrib["srcmd5"] = i.srcmd5
4850+
ET.SubElement(entry, "author").text = i.user
4851+
ET.SubElement(entry, "date").text = i.get_time_str()
4852+
ET.SubElement(entry, "requestid").text = str(i.requestid) if i.requestid else ""
4853+
ET.SubElement(entry, "msg").text = i.comment or ""
48534854
xmlindent(root)
48544855
yield from ET.tostring(root, encoding="utf-8").decode("utf-8").splitlines()
48554856
return
48564857

48574858
if format == "text":
4858-
for revision in reversed(revision_list):
4859+
for i in reversed(revision_list):
48594860
entry = (
4860-
f"r{revision.rev}",
4861-
revision.user,
4862-
revision.get_time_str(),
4863-
revision.srcmd5,
4864-
revision.version,
4865-
f"rq{revision.requestid}" if revision.requestid else ""
4861+
f"r{i.rev}",
4862+
i.user,
4863+
i.get_time_str(),
4864+
i.srcmd5,
4865+
i.version,
4866+
f"rq{i.requestid}" if i.requestid else ""
48664867
)
48674868
yield 76 * "-"
48684869
yield " | ".join(entry)
48694870
yield ""
4870-
yield revision.comment or "<no message>"
4871+
yield i.comment or "<no message>"
48714872
yield ""
48724873
if patch:
48734874
rdiff = server_diff_noex(
48744875
apiurl,
48754876
prj,
48764877
package,
4877-
revision.rev - 1,
4878+
str(i.rev - 1),
48784879
prj,
48794880
package,
4880-
revision.rev,
4881+
str(i.rev),
48814882
meta=meta,
48824883
)
48834884
yield highlight_diff(rdiff).decode("utf-8", errors="replace")

osc/meter.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ def create_text_meter(*args, **kwargs) -> TextMeterBase:
102102

103103
use_pb_fallback = kwargs.pop("use_pb_fallback", False)
104104

105-
meter_class: TextMeterBase
106105
if config.quiet:
107106
meter_class = NoTextMeter
108107
elif not have_pb_module or not config.show_download_progress or not sys.stdout.isatty() or use_pb_fallback:

osc/output/output.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import subprocess
66
import sys
77
import tempfile
8+
from typing import BinaryIO
89
from typing import Dict
10+
from typing import Generator
911
from typing import List
1012
from typing import Optional
1113
from typing import TextIO
@@ -137,7 +139,7 @@ def safe_print(*args, **kwargs):
137139
print(*args, **kwargs)
138140

139141

140-
def safe_write(file: TextIO, text: Union[str, bytes], *, add_newline: bool = False):
142+
def safe_write(file: Union[BinaryIO, TextIO], text: Union[str, bytes], *, add_newline: bool = False):
141143
"""
142144
Run sanitize_text() on ``text`` and write it to ``file``.
143145
@@ -211,7 +213,7 @@ def run_pager(message: Union[bytes, str], tmp_suffix: str = ""):
211213
run_external(*cmd, env=env)
212214

213215

214-
def pipe_to_pager(lines: Union[List[bytes], List[str]], *, add_newlines=False):
216+
def pipe_to_pager(lines: Union[List[bytes], List[str], Generator[bytes, None, None], Generator[str, None, None]], *, add_newlines=False):
215217
"""
216218
Pipe ``lines`` to the pager.
217219
If running in a non-interactive terminal, print the data instead.

osc/util/ar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __str__(self):
3737
class ArHdr:
3838
"""Represents an ar header entry"""
3939

40-
def __init__(self, fn: bytes, date: bytes, uid: bytes, gid: bytes, mode: bytes, size: bytes, fmag: bytes, off: bytes):
40+
def __init__(self, fn: bytes, date: bytes, uid: bytes, gid: bytes, mode: bytes, size: bytes, fmag: bytes, off: int):
4141
self.file = fn.strip()
4242
self.date = date.strip()
4343
self.uid = uid.strip()

osc/util/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ def xml_request(
792792
apiurl: str,
793793
path: List[str],
794794
query: Optional[dict] = None,
795-
headers: Optional[str] = None,
795+
headers: Optional[dict] = None,
796796
data: Optional[str] = None,
797797
) -> urllib3.response.HTTPResponse:
798798
from ..connection import http_request

osc/util/safewriter.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,39 @@
1+
import io
2+
13
# be careful when debugging this code:
24
# don't add print statements when setting sys.stdout = SafeWriter(sys.stdout)...
3-
class SafeWriter:
5+
class SafeWriter(io.TextIOBase):
46
"""
57
Safely write an (unicode) str. In case of an "UnicodeEncodeError" the
68
the str is encoded with the "encoding" encoding.
79
All getattr, setattr calls are passed through to the "writer" instance.
810
"""
911

1012
def __init__(self, writer, encoding='unicode_escape'):
13+
super().__init__()
1114
self._writer = writer
1215
self._encoding = encoding
1316

17+
# TextIOBase requires overriding the following stub methods: detach, read, readline, and write
18+
19+
def detach(self, *args, **kwargs):
20+
return self._writer.detach(*args, **kwargs)
21+
22+
def read(self, *args, **kwargs):
23+
return self._writer.read(args, **kwargs)
24+
25+
def readline(self, *args, **kwargs):
26+
return self._writer.readline(args, **kwargs)
27+
1428
def write(self, s):
1529
try:
1630
self._writer.write(s)
1731
except UnicodeEncodeError as e:
1832
self._writer.write(s.encode(self._encoding))
1933

34+
def fileno(self, *args, **kwargs):
35+
return self._writer.fileno(*args, **kwargs)
36+
2037
def __getattr__(self, name):
2138
return getattr(self._writer, name)
2239

0 commit comments

Comments
 (0)