Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions iotlabcli/associations.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@

import abc
import collections.abc
from collections.abc import Callable
from typing import Any


def _disabled_method(*_):
def _disabled_method(*_: Any) -> None:
"""Disabled method."""
raise AttributeError


def setattrdefault(obj, attribute, default=None):
def setattrdefault(obj: Any, attribute: str, default: Any = None) -> Any:
"""Setdefault for attribute.

Returns 'attribute' value if defined or set it to default and return it.
Expand Down Expand Up @@ -69,7 +71,7 @@ class _Association(
VALUE = None
VALUE_SORT_KEY = None

def __init__(self, key, value): # pylint:disable=super-init-not-called
def __init__(self, key: Any, value: Any) -> None: # pylint:disable=super-init-not-called
# Don't call 'dict' init, only used for json dumping
self._concrete_class()
self.key = key
Expand Down Expand Up @@ -135,15 +137,17 @@ def from_dict(cls, assocdict):
return cls(assocdict[cls._keyattr()], assocdict[cls._valueattr()])

@staticmethod
def staticclassattribute(function):
def staticclassattribute(function: Callable[..., Any] | None) -> Any:
"""Return given function as a staticmethod, handle None as None.

This allows storing the function as a class attribute.
"""
return staticmethod(function) if function is not None else None

@classmethod
def for_key_value(cls, key, value, sortkey=None):
def for_key_value(
cls, key: str, value: str, sortkey: Callable[..., Any] | None = None
) -> type["_Association"]:
"""Create association class for assoctype."""
name = f"{key.title()}{value.title()}Association"

Expand Down Expand Up @@ -222,12 +226,14 @@ class AssociationsMap(
Inherit list to be json serialized to a list.
"""

def __init__(self, assoctype, resource, sortkey=None):
def __init__(
self, assoctype: str, resource: str, sortkey: Callable[..., Any] | None = None
) -> None:
# pylint:disable=super-init-not-called

list.__init__(self)
self.assoc_class = _Association.for_key_value(assoctype, resource, sortkey)
self._map = {}
self._map: dict[Any, Any] = {}

def __getitem__(self, key):
return self._map[key].value
Expand All @@ -244,7 +250,7 @@ def __setitem__(self, key, value):
except KeyError:
self._add(key, value)

def extendvalues(self, key, values):
def extendvalues(self, key: Any, values: Any) -> Any:
"""Extend values for `key`."""
self.setdefault(key, []).extend(values)
return self[key]
Expand All @@ -261,7 +267,13 @@ def _add(self, key, value):
list.sort(self, key=lambda x: x.key)

@classmethod
def from_list(cls, assoclist, assoctype, resource, sortkey=None):
def from_list(
cls,
assoclist: list[Any] | None,
assoctype: str,
resource: str,
sortkey: Callable[..., Any] | None = None,
) -> "AssociationsMap | None":
"""Create AssociationsMap from assoclist."""
if assoclist is None:
return None
Expand Down Expand Up @@ -306,7 +318,11 @@ def items(self):
__setslice__ = property(_disabled_method)


def associationsmapdict_from_dict(assocsdict, resource, sortkey=None):
def associationsmapdict_from_dict(
assocsdict: dict[str, Any] | None,
resource: str,
sortkey: Callable[..., Any] | None = None,
) -> dict[str, AssociationsMap] | None:
"""Create a dict of AssociationsMap from `assocsdict` for `resource`."""
if assocsdict is None:
return None
Expand Down
18 changes: 10 additions & 8 deletions iotlabcli/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@
"""Authentication file management"""

import getpass
import logging
import os
import os.path
from base64 import b64decode, b64encode

from iotlabcli.rest import Api
import logging

RC_FILE = os.getenv("IOTLAB_PASSWORD_FILE") or os.path.expanduser("~/.iotlabrc")


def get_user_credentials(username=None, password=None):
def get_user_credentials(
username: str | None = None, password: str | None = None
) -> tuple[str | None, str | None]:
"""Return user credentials.
If provided in arguments return them, if password missing, ask on console,
or try to read them from password file"""
Expand All @@ -46,13 +48,13 @@ def get_user_credentials(username=None, password=None):
return username, password


def check_user_credentials(username, password):
def check_user_credentials(username: str, password: str) -> bool:
"""Check that the given credentials are valid"""
api = Api(username, password)
return api.check_credential()


def write_password_file(username, password):
def write_password_file(username: str, password: str) -> None:
"""Create a password file for basic authentication http when
command-line option username and password are used We write .iotlabrc
file in user home directory with format username:base64(password)
Expand All @@ -69,14 +71,14 @@ def write_password_file(username, password):
pass_file.write(f"{username}:{enc_password}")


def _read_password_file():
def _read_password_file() -> tuple[str | None, str | None]:
"""Try to read password file (.iotlabrc) in user home directory when
command-line option username and password are not used. If password
file exist whe return username and password for basic auth http
authentication
"""
if not os.path.exists(RC_FILE):
logging.info(f"Reading password file: no such file or directory: '{RC_FILE}'")
logging.info("Reading password file: no such file or directory: '%s'", RC_FILE)
return None, None
try:
with open(RC_FILE, "r") as password_file:
Expand All @@ -91,7 +93,7 @@ def _read_password_file():
IDENTITY_FILE = os.path.expanduser("~/.ssh/id_rsa")


def add_ssh_key(identity_file=None):
def add_ssh_key(identity_file: str | None = None) -> None:
"""Install ssh key into user's iot-lab account"""

if identity_file is None:
Expand All @@ -113,7 +115,7 @@ def add_ssh_key(identity_file=None):
api.set_ssh_keys(keys_json)


def ssh_keys():
def ssh_keys() -> None:
"""List ssh keys configured into user's iot-lab account"""

api = Api(*get_user_credentials())
Expand Down
Loading
Loading