Skip to content

Commit 5dbe212

Browse files
authored
SCANPY-160 add mypy to the ci (#185)
1 parent 1c37419 commit 5dbe212

16 files changed

+179
-141
lines changed

.cirrus.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ analysis_linux_task:
165165
alias: analysis
166166
name: "NEXT Analysis"
167167
analysis_script:
168+
- poetry install
168169
- poetry run pytest --cov-report=xml:coverage.xml --cov-config=pyproject.toml --cov=src --cov-branch tests
170+
- poetry run mypy src/ > mypy-report.txt || true # mypy exits with 1 if there are errors
169171
- uv venv
170172
- source .venv/bin/activate
171173
- uv pip install pysonar-scanner

poetry.lock

Lines changed: 97 additions & 77 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pytest-cov = '6.1.0'
4747
pytest-subtests = "^0.14.1"
4848
pytest-docker = "^3.2.0"
4949
debugpy = "^1.8.13"
50+
types-requests = "^2.32.0.20250328"
5051

5152
[[tool.poetry.packages]]
5253
from = 'src'

sonar-project.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ sonar.projectName=Python Scanner
33
sonar.projectVersion=1.0.0
44
sonar.python.version=3.9,3.10,3.11,3.12,3.13
55
sonar.python.coverage.reportPaths=coverage.xml
6+
sonar.python.mypy.reportPaths=mypy-report.txt
67
sonar.analysisCache.enabled=true
78
sonar.sources=src
89
sonar.tests=tests

src/pysonar_scanner/__main__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#
2020

2121
import logging
22+
from typing import Any
2223
from pysonar_scanner import app_logging
2324
from pysonar_scanner import cache
2425
from pysonar_scanner import exceptions
@@ -71,7 +72,7 @@ def set_logging_options(config):
7172
app_logging.configure_logging_level(verbose=config.get(SONAR_VERBOSE, False))
7273

7374

74-
def build_api(config: dict[str, any]) -> SonarQubeApi:
75+
def build_api(config: dict[str, Any]) -> SonarQubeApi:
7576
token = configuration_loader.get_token(config)
7677
base_urls = get_base_urls(config)
7778
return SonarQubeApi(base_urls, token)
@@ -109,7 +110,7 @@ def create_scanner_engine(api, cache_manager, config):
109110
return scanner
110111

111112

112-
def create_jre(api, cache, config: dict[str, any]) -> JREResolvedPath:
113+
def create_jre(api, cache, config: dict[str, Any]) -> JREResolvedPath:
113114
jre_provisioner = JREProvisioner(api, cache, config[SONAR_SCANNER_OS], config[SONAR_SCANNER_ARCH])
114115
jre_resolver = JREResolver(JREResolverConfiguration.from_dict(config), jre_provisioner)
115116
return jre_resolver.resolve_jre()

src/pysonar_scanner/api.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#
2020
import typing
2121
from dataclasses import dataclass
22-
from typing import NoReturn, Optional, TypedDict
22+
from typing import Any, NoReturn, Optional
2323

2424
import requests
2525
import requests.auth
@@ -118,33 +118,35 @@ def from_dict(dict: dict) -> "JRE":
118118
)
119119

120120

121-
ApiConfiguration = TypedDict(
122-
"ApiConfiguration",
123-
{SONAR_HOST_URL: str, SONAR_SCANNER_SONARCLOUD_URL: str, SONAR_SCANNER_API_BASE_URL: str, SONAR_REGION: str},
124-
)
121+
@dataclass(frozen=True)
122+
class ApiConfiguration:
123+
sonar_host_url: str
124+
sonar_scanner_sonarcloud_url: str
125+
sonar_scanner_api_base_url: str
126+
sonar_region: str
125127

126128

127-
def to_api_configuration(config_dict: dict[Key, any]) -> ApiConfiguration:
128-
return {
129-
SONAR_HOST_URL: config_dict.get(SONAR_HOST_URL, ""),
130-
SONAR_SCANNER_SONARCLOUD_URL: config_dict.get(SONAR_SCANNER_SONARCLOUD_URL, ""),
131-
SONAR_SCANNER_API_BASE_URL: config_dict.get(SONAR_SCANNER_API_BASE_URL, ""),
132-
SONAR_REGION: config_dict.get(SONAR_REGION, ""),
133-
}
129+
def to_api_configuration(config_dict: dict[Key, Any]) -> ApiConfiguration:
130+
return ApiConfiguration(
131+
sonar_host_url=config_dict.get(SONAR_HOST_URL, ""),
132+
sonar_scanner_sonarcloud_url=config_dict.get(SONAR_SCANNER_SONARCLOUD_URL, ""),
133+
sonar_scanner_api_base_url=config_dict.get(SONAR_SCANNER_API_BASE_URL, ""),
134+
sonar_region=config_dict.get(SONAR_REGION, ""),
135+
)
134136

135137

136-
def get_base_urls(config_dict: dict[Key, any]) -> BaseUrls:
138+
def get_base_urls(config_dict: dict[Key, Any]) -> BaseUrls:
137139
def is_sq_cloud_url(api_config: ApiConfiguration, sonar_host_url: str) -> bool:
138-
sq_cloud_url = api_config[SONAR_SCANNER_SONARCLOUD_URL] or GLOBAL_SONARCLOUD_URL
140+
sq_cloud_url = api_config.sonar_scanner_sonarcloud_url or GLOBAL_SONARCLOUD_URL
139141
return remove_trailing_slash(sonar_host_url) in [remove_trailing_slash(sq_cloud_url), US_SONARCLOUD_URL]
140142

141143
def is_blank(str) -> bool:
142144
return str.strip() == ""
143145

144146
api_config: ApiConfiguration = to_api_configuration(config_dict)
145147

146-
sonar_host_url = remove_trailing_slash(api_config[SONAR_HOST_URL])
147-
region = api_config[SONAR_REGION]
148+
sonar_host_url = remove_trailing_slash(api_config.sonar_host_url)
149+
region = api_config.sonar_region
148150
if region and region != "us":
149151
raise InconsistentConfiguration(
150152
f"Invalid region '{region}'. Valid regions are: 'us'. "
@@ -158,11 +160,11 @@ def is_blank(str) -> bool:
158160
if is_blank(sonar_host_url) or is_sq_cloud_url(api_config, sonar_host_url):
159161
default_url = US_SONARCLOUD_URL if region == "us" else GLOBAL_SONARCLOUD_URL
160162
default_api_base_url = "https://api.sonarqube.us" if region == "us" else "https://api.sonarcloud.io"
161-
sonar_host_url = api_config[SONAR_SCANNER_SONARCLOUD_URL] or default_url
162-
api_base_url = api_config[SONAR_SCANNER_API_BASE_URL] or default_api_base_url
163+
sonar_host_url = api_config.sonar_scanner_sonarcloud_url or default_url
164+
api_base_url = api_config.sonar_scanner_api_base_url or default_api_base_url
163165
return BaseUrls(base_url=sonar_host_url, api_base_url=api_base_url, is_sonar_qube_cloud=True)
164166
else:
165-
api_base_url = api_config[SONAR_SCANNER_API_BASE_URL] or f"{sonar_host_url}/api/v2"
167+
api_base_url = api_config.sonar_scanner_api_base_url or f"{sonar_host_url}/api/v2"
166168
return BaseUrls(base_url=sonar_host_url, api_base_url=api_base_url, is_sonar_qube_cloud=False)
167169

168170

src/pysonar_scanner/configuration/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
#
2020
import argparse
21+
from typing import Any
2122

2223
from pysonar_scanner.configuration import properties
2324
from pysonar_scanner.exceptions import UnexpectedCliArgument
@@ -26,7 +27,7 @@
2627
class CliConfigurationLoader:
2728

2829
@classmethod
29-
def load(cls) -> dict[str, any]:
30+
def load(cls) -> dict[str, Any]:
3031
args, unknown_args = cls.__parse_cli_args()
3132
config = {}
3233
for prop in properties.PROPERTIES:

src/pysonar_scanner/configuration/configuration_loader.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#
2020
import logging
2121
from pathlib import Path
22+
from typing import Any
2223

2324
from pysonar_scanner.configuration.cli import CliConfigurationLoader
2425
from pysonar_scanner.configuration.pyproject_toml import TomlConfigurationLoader
@@ -29,13 +30,13 @@
2930
from pysonar_scanner.exceptions import MissingProperty, MissingPropertyException
3031

3132

32-
def get_static_default_properties() -> dict[Key, any]:
33+
def get_static_default_properties() -> dict[Key, Any]:
3334
return {prop.name: prop.default_value for prop in PROPERTIES if prop.default_value is not None}
3435

3536

3637
class ConfigurationLoader:
3738
@staticmethod
38-
def load() -> dict[Key, any]:
39+
def load() -> dict[Key, Any]:
3940
logging.debug("Loading configuration properties...")
4041

4142
# each property loader is required to return NO default values.
@@ -60,7 +61,7 @@ def load() -> dict[Key, any]:
6061
return resolved_properties
6162

6263
@staticmethod
63-
def check_configuration(config: dict[Key, any]) -> None:
64+
def check_configuration(config: dict[Key, Any]) -> None:
6465
missing_keys = []
6566
if SONAR_TOKEN not in config:
6667
missing_keys.append(MissingProperty(SONAR_TOKEN, "--token"))
@@ -72,7 +73,7 @@ def check_configuration(config: dict[Key, any]) -> None:
7273
raise MissingPropertyException.from_missing_keys(*missing_keys)
7374

7475

75-
def get_token(config: dict[Key, any]) -> str:
76+
def get_token(config: dict[Key, Any]) -> str:
7677
if SONAR_TOKEN not in config:
7778
raise MissingPropertyException(f'Missing property "{SONAR_TOKEN}"')
7879
return config[SONAR_TOKEN]

src/pysonar_scanner/configuration/dynamic_defaults_loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
#
2020
import os
21-
from typing import Dict
21+
from typing import Any, Dict
2222

2323
from pysonar_scanner.configuration.properties import Key, SONAR_SCANNER_OS, SONAR_SCANNER_ARCH, SONAR_PROJECT_BASE_DIR
2424
from pysonar_scanner import utils
2525

2626

27-
def load() -> Dict[Key, any]:
27+
def load() -> Dict[Key, Any]:
2828
"""
2929
Load dynamically computed default properties
3030
"""

src/pysonar_scanner/configuration/properties.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import time
2121
import argparse
2222
from dataclasses import dataclass
23-
from typing import Callable, Optional
23+
from typing import Any, Callable, Optional
2424

2525
Key = str
2626

@@ -106,10 +106,10 @@ class Property:
106106
name: Key
107107
"""name in the format of `sonar.scanner.appVersion`"""
108108

109-
default_value: Optional[any]
109+
default_value: Optional[Any]
110110
"""default value for the property; if None, no default value is set"""
111111

112-
cli_getter: Optional[Callable[[argparse.Namespace], any]] = None
112+
cli_getter: Optional[Callable[[argparse.Namespace], Any]] = None
113113
"""function to get the value from the CLI arguments namespace. If None, the property is not settable via CLI"""
114114

115115
def python_name(self) -> str:

0 commit comments

Comments
 (0)