Skip to content

Commit

Permalink
Add Ruff
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrunner committed Dec 3, 2024
1 parent 2f733d3 commit 68a411f
Show file tree
Hide file tree
Showing 44 changed files with 195 additions and 135 deletions.
3 changes: 0 additions & 3 deletions .bandit.yaml

This file was deleted.

29 changes: 7 additions & 22 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,39 +61,24 @@ repos:
rev: v0.1.8
hooks:
- id: ripsecrets
- repo: https://github.com/PyCQA/autoflake
rev: v2.3.1
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.1
hooks:
- id: autoflake
- repo: https://github.com/asottile/pyupgrade
rev: v3.19.0
hooks:
- id: pyupgrade
args:
- --py39-plus
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
# isort issue: https://github.com/PyCQA/isort/issues/1889
- id: ruff-format
args:
- --project=c2cwsgiutils
- repo: https://github.com/psf/black
rev: 24.10.0
hooks:
- id: black
exclude: .*\.html
- --line-length=110
- repo: https://github.com/PyCQA/prospector
rev: v1.13.3
hooks:
- id: prospector
args:
- --tool=pydocstyle
- --tool=ruff
- --die-on-tool-error
- --output-format=pylint
additional_dependencies:
- prospector-profile-duplicated==1.8.0 # pypi
- prospector-profile-utils==1.12.2 # pypi
- prospector-profile-utils==1.13.0 # pypi
- ruff==0.8.1 # pypi
- repo: https://github.com/sbrunner/jsonschema-validator
rev: 0.3.2
hooks:
Expand Down
25 changes: 2 additions & 23 deletions .prospector.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
inherits:
- utils:base
- utils:no-design-checks
- utils:c2cwsgiutils
- utils:fix
- utils:unsafe
- duplicated

doc-warnings: true

ignore-paths:
- docs
- acceptance_tests
Expand All @@ -16,24 +16,3 @@ pylint:
extension-pkg-allow-list:
- ujson
- lxml
disable:
- no-else-return
- no-else-raise
- missing-module-docstring
- missing-timeout # A default timeout is set

pydocstyle:
disable:
- D104 # Missing docstring in public package
- D105 # Missing docstring in magic method
- D107 # Missing docstring in __init__

pycodestyle:
disable:
# Buggy checks with Python 3.12
- E221 # multiple spaces before operator
- E702 # multiple statements on one line (semicolon)

bandit:
options:
config: .bandit.yaml
1 change: 1 addition & 0 deletions c2cwsgiutils/acceptance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def retry(
tries: number of times to try (not retry) before giving up
delay: initial delay between retries in seconds
backoff: backoff multiplier e.g. value of 2 will double the delay each retry
"""

def deco_retry(f: typing.Callable[..., typing.Any]) -> typing.Callable[..., typing.Any]:
Expand Down
5 changes: 3 additions & 2 deletions c2cwsgiutils/acceptance/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Connection:
"""The connection."""

def __init__(self, base_url: str, origin: str) -> None:
"""Initialize the connection."""
self.base_url = base_url
if not self.base_url.endswith("/"):
self.base_url += "/"
Expand Down Expand Up @@ -93,10 +94,10 @@ def get_xml(
check_response(r, expected_status, cache_expected=cache_expected)
self._check_cors(cors, r)
r.raw.decode_content = True
doc = etree.parse(r.raw) # nosec
doc = etree.parse(r.raw) # noqa: S320
if schema is not None:
with open(schema, encoding="utf-8") as schema_file:
xml_schema = etree.XMLSchema(etree.parse(schema_file)) # nosec
xml_schema = etree.XMLSchema(etree.parse(schema_file)) # noqa: S320
xml_schema.assertValid(doc)
return doc

Expand Down
6 changes: 4 additions & 2 deletions c2cwsgiutils/acceptance/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def check_image(
level: The minimum similarity level (between 0.0 and 1.0), default to 1.0
generate_expected_image: If `True` generate the expected image instead of checking it
use_mask: If `False` don't use the mask event if the file exists
"""
assert image_to_check is not None, "Image required"
image_file_basename = os.path.splitext(os.path.basename(expected_filename))[0]
Expand Down Expand Up @@ -122,7 +123,7 @@ def check_image(
if np.issubdtype(mask.dtype, np.floating):
mask = (mask * 255).astype("uint8")

assert ((0 < mask) & (mask < 255)).sum() == 0, "Mask should be only black and white image"
assert ((mask > 0) & (mask < 255)).sum() == 0, "Mask should be only black and white image"

# Convert to boolean
mask = mask == 0
Expand All @@ -139,7 +140,7 @@ def check_image(
return
if not os.path.isfile(expected_filename):
skimage.io.imsave(expected_filename, image_to_check)
assert False, "Expected image not found: " + expected_filename
raise AssertionError("Expected image not found: " + expected_filename)
expected = skimage.io.imread(expected_filename)
assert expected is not None, "Wrong image: " + expected_filename
expected = normalize_image(expected)
Expand Down Expand Up @@ -201,6 +202,7 @@ def check_screenshot(
level: See `check_image`
generate_expected_image: See `check_image`
use_mask: See `check_image`
"""
if headers is None:
headers = {}
Expand Down
1 change: 1 addition & 0 deletions c2cwsgiutils/acceptance/print.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def __init__(self, base_url: str, origin: str) -> None:
base_url: The base URL to the print server (including the /print)
app: The name of the application to use
origin: The origin and referrer to include in the requests
"""
super().__init__(base_url=base_url, origin=origin)
self.session.headers["Referrer"] = origin
Expand Down
3 changes: 2 additions & 1 deletion c2cwsgiutils/acceptance/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def retry_timeout(what: Callable[[], Any], timeout: float = _DEFAULT_TIMEOUT, in
what: the function to try
timeout: the timeout to get a success
interval: the interval between try
"""
timeout = time.perf_counter() + timeout
while True:
Expand All @@ -46,7 +47,7 @@ def retry_timeout(what: Callable[[], Any], timeout: float = _DEFAULT_TIMEOUT, in
error = str(e)
_LOG.info(" Failed: %s", e)
if time.perf_counter() > timeout:
assert False, "Timeout: " + error
raise AssertionError("Timeout: " + error)
time.sleep(interval)


Expand Down
17 changes: 9 additions & 8 deletions c2cwsgiutils/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,32 @@
from c2cwsgiutils.config_utils import config_bool, env_or_config, env_or_settings

_COOKIE_AGE = 7 * 24 * 3600
SECRET_PROP = "c2c.secret" # nosec # noqa
SECRET_ENV = "C2C_SECRET" # nosec # noqa
SECRET_PROP = "c2c.secret" # noqa: S105
SECRET_ENV = "C2C_SECRET" # noqa: S105
_GITHUB_REPOSITORY_PROP = "c2c.auth.github.repository"
_GITHUB_REPOSITORY_ENV = "C2C_AUTH_GITHUB_REPOSITORY"
_GITHUB_ACCESS_TYPE_PROP = "c2c.auth.github.access_type"
_GITHUB_ACCESS_TYPE_ENV = "C2C_AUTH_GITHUB_ACCESS_TYPE"
GITHUB_AUTH_URL_PROP = "c2c.auth.github.auth_url"
GITHUB_AUTH_URL_ENV = "C2C_AUTH_GITHUB_AUTH_URL"
GITHUB_TOKEN_URL_PROP = "c2c.auth.github.token_url" # nosec
GITHUB_TOKEN_URL_ENV = "C2C_AUTH_GITHUB_TOKEN_URL" # nosec
GITHUB_TOKEN_URL_PROP = "c2c.auth.github.token_url" # noqa: S105
GITHUB_TOKEN_URL_ENV = "C2C_AUTH_GITHUB_TOKEN_URL" # noqa: S105
GITHUB_USER_URL_PROP = "c2c.auth.github.user_url"
GITHUB_USER_URL_ENV = "C2C_AUTH_GITHUB_USER_URL"
_GITHUB_REPO_URL_PROP = "c2c.auth.github.repo_url"
_GITHUB_REPO_URL_ENV = "C2C_AUTH_GITHUB_REPO_URL"
GITHUB_CLIENT_ID_PROP = "c2c.auth.github.client_id"
GITHUB_CLIENT_ID_ENV = "C2C_AUTH_GITHUB_CLIENT_ID"
GITHUB_CLIENT_SECRET_PROP = "c2c.auth.github.client_secret" # nosec # noqa
GITHUB_CLIENT_SECRET_ENV = "C2C_AUTH_GITHUB_CLIENT_SECRET" # nosec # noqa
GITHUB_CLIENT_SECRET_PROP = "c2c.auth.github.client_secret" # noqa: S105
GITHUB_CLIENT_SECRET_ENV = "C2C_AUTH_GITHUB_CLIENT_SECRET" # noqa: S105
GITHUB_SCOPE_PROP = "c2c.auth.github.scope"
GITHUB_SCOPE_ENV = "C2C_AUTH_GITHUB_SCOPE"
# To be able to use private repository
GITHUB_SCOPE_DEFAULT = "repo"
GITHUB_AUTH_COOKIE_PROP = "c2c.auth.github.auth.cookie"
GITHUB_AUTH_COOKIE_ENV = "C2C_AUTH_GITHUB_COOKIE"
GITHUB_AUTH_SECRET_PROP = "c2c.auth.github.auth.secret" # nosec # noqa
GITHUB_AUTH_SECRET_ENV = "C2C_AUTH_GITHUB_SECRET" # nosec # noqa
GITHUB_AUTH_SECRET_PROP = "c2c.auth.github.auth.secret" # noqa: S105
GITHUB_AUTH_SECRET_ENV = "C2C_AUTH_GITHUB_SECRET" # noqa: S105
GITHUB_AUTH_PROXY_URL_PROP = "c2c.auth.github.auth.proxy_url"
GITHUB_AUTH_PROXY_URL_ENV = "C2C_AUTH_GITHUB_PROXY_URL"
USE_SESSION_PROP = "c2c.use_session"
Expand Down Expand Up @@ -209,6 +209,7 @@ def check_access(
request: is the request object.
repo: is the repository to check access to (<organization>/<repository>).
access_type: is the type of access to check (admin|push|pull).
"""
if not is_auth(request):
return False
Expand Down
2 changes: 1 addition & 1 deletion c2cwsgiutils/broadcast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

def init(config: Optional[pyramid.config.Configurator] = None) -> None:
"""Initialize the broadcaster with Redis, if configured, for backward compatibility."""
warnings.warn("init function is deprecated; use includeme instead")
warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
includeme(config)


Expand Down
4 changes: 4 additions & 0 deletions c2cwsgiutils/broadcast/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@ class LocalBroadcaster(interface.BaseBroadcaster):
"""Fake implementation of broadcasting messages (will just answer locally)."""

def __init__(self) -> None:
"""Initialize the broadcaster."""
self._subscribers: MutableMapping[str, Callable[..., Any]] = {}

def subscribe(self, channel: str, callback: Callable[..., Any]) -> None:
"""Subscribe to a channel."""
self._subscribers[channel] = callback

def unsubscribe(self, channel: str) -> None:
"""Unsubscribe from a channel."""
del self._subscribers[channel]

def broadcast(
self, channel: str, params: Mapping[str, Any], expect_answers: bool, timeout: float
) -> Optional[list[Any]]:
"""Broadcast a message to all the listeners."""
subscriber = self._subscribers.get(channel, None)
answers = [utils.add_host_info(subscriber(**params))] if subscriber is not None else []
return answers if expect_answers else None
Expand Down
10 changes: 8 additions & 2 deletions c2cwsgiutils/broadcast/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def __init__(
master: "redis.client.Redis[str]",
slave: "redis.client.Redis[str]",
) -> None:
"""Initialize the broadcaster."""
from c2cwsgiutils import redis_utils # pylint: disable=import-outside-toplevel

self._master = master
Expand All @@ -40,6 +41,8 @@ def _get_channel(self, channel: str) -> str:
return self._broadcast_prefix + channel

def subscribe(self, channel: str, callback: Callable[..., Any]) -> None:
"""Subscribe to a channel."""

def wrapper(message: Mapping[str, Any]) -> None:
_LOG.debug("Received a broadcast on %s: %s", message["channel"], repr(message["data"]))
data = json.loads(message["data"])
Expand All @@ -58,13 +61,15 @@ def wrapper(message: Mapping[str, Any]) -> None:
self._pub_sub.subscribe(**{actual_channel: wrapper})

def unsubscribe(self, channel: str) -> None:
"""Unsubscribe from a channel."""
_LOG.debug("Unsubscribing from %s")
actual_channel = self._get_channel(channel)
self._pub_sub.unsubscribe(actual_channel)

def broadcast(
self, channel: str, params: Mapping[str, Any], expect_answers: bool, timeout: float
) -> Optional[list[Any]]:
"""Broadcast a message to all the listeners."""
if expect_answers:
return self._broadcast_with_answer(channel, params, timeout)
else:
Expand All @@ -85,7 +90,8 @@ def callback(msg: Mapping[str, Any]) -> None:
cond.notify()

answer_channel = self._get_channel(channel) + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(10) # nosec
random.choice(string.ascii_uppercase + string.digits) # noqa: S311
for _ in range(10)
)
_LOG.debug("Subscribing for broadcast answers on %s", answer_channel)
self._pub_sub.subscribe(**{answer_channel: callback})
Expand All @@ -98,7 +104,7 @@ def callback(msg: Mapping[str, Any]) -> None:
with cond:
while len(answers) < nb_received:
to_wait = timeout_time - time.perf_counter()
if to_wait <= 0.0: # pragma: no cover
if to_wait <= 0.0:
_LOG.warning(
"timeout waiting for %d/%d answers on %s",
len(answers),
Expand Down
2 changes: 2 additions & 0 deletions c2cwsgiutils/client_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ class Filter:
"""

def __init__(self, application: Callable[[dict[str, str], Any], Any]):
"""Initialize the filter."""
self._application = application

def __call__(self, environ: dict[str, str], start_response: Any) -> Any:
"""Update the environ with the headers."""
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
if "HTTP_FORWARDED" in environ:
_handle_forwarded(environ)
Expand Down
4 changes: 2 additions & 2 deletions c2cwsgiutils/coverage_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

def init() -> None:
"""Initialize the code coverage, for backward compatibility."""
warnings.warn("init function is deprecated; use includeme instead")
warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
includeme()


Expand All @@ -22,7 +22,7 @@ def includeme(config: Optional[pyramid.config.Configurator] = None) -> None:
import coverage # pylint: disable=import-outside-toplevel

_LOG.warning("Setting up code coverage")
report_dir = "/tmp/coverage/api" # nosec
report_dir = "/tmp/coverage/api" # noqa: S108
os.makedirs(report_dir, exist_ok=True)
cov = coverage.Coverage(
data_file=os.path.join(report_dir, "coverage"),
Expand Down
13 changes: 11 additions & 2 deletions c2cwsgiutils/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,11 @@ def setup_session(
force_slave: The method/paths that needs to use the slave
Returns: The SQLAlchemy session, the R/W engine and the R/O engine
"""
warnings.warn("setup_session function is deprecated; use init and request.dbsession instead")
warnings.warn(
"setup_session function is deprecated; use init and request.dbsession instead", stacklevel=2
)
if slave_prefix is None:
slave_prefix = master_prefix
settings = config.registry.settings
Expand Down Expand Up @@ -122,8 +125,11 @@ def create_session(
engine_config: The rest of the parameters are passed as is to the sqlalchemy.create_engine function
Returns: The SQLAlchemy session
"""
warnings.warn("create_session function is deprecated; use init and request.dbsession instead")
warnings.warn(
"create_session function is deprecated; use init and request.dbsession instead", stacklevel=2
)
if slave_url is None:
slave_url = url

Expand Down Expand Up @@ -215,6 +221,7 @@ def __init__(
ro_engine: sqlalchemy.engine.Engine,
rw_engine: sqlalchemy.engine.Engine,
):
"""Initialize the session factory."""
super().__init__()
self.master_paths: Iterable[Pattern[str]] = (
list(map(_RE_COMPILE, force_master)) if force_master else []
Expand All @@ -232,6 +239,7 @@ def engine_name(self, readwrite: bool) -> str:
def __call__( # type: ignore
self, request: Optional[pyramid.request.Request], readwrite: Optional[bool] = None, **local_kw: Any
) -> _scoped_session:
"""Set the engine based on the request."""
if readwrite is not None:
if readwrite and not FORCE_READONLY:
_LOG.debug("Using %s database", self.rw_engine.c2c_name) # type: ignore
Expand Down Expand Up @@ -374,6 +382,7 @@ def init(
force_slave: The method/paths that needs to use the slave
Returns: The SQLAlchemy session
"""
settings = config.get_settings()
settings["tm.manager_hook"] = "pyramid_tm.explicit_manager"
Expand Down
2 changes: 1 addition & 1 deletion c2cwsgiutils/db_maintenance_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

def install_subscriber(config: pyramid.config.Configurator) -> None:
"""Install the view to configure the loggers, if configured to do so, for backward compatibility."""
warnings.warn("install_subscriber function is deprecated; use includeme instead")
warnings.warn("install_subscriber function is deprecated; use includeme instead", stacklevel=2)
includeme(config)


Expand Down
Loading

0 comments on commit 68a411f

Please sign in to comment.