diff --git a/.github/actions/install-deps/action.yml b/.github/actions/install-deps/action.yml index fd10b91..3bd1f2a 100644 --- a/.github/actions/install-deps/action.yml +++ b/.github/actions/install-deps/action.yml @@ -27,5 +27,4 @@ runs: - name: Install dependencies with uv shell: bash - run: | - uv sync + run: uv sync diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d3e118..f271524 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,9 +16,9 @@ repos: language: system types: [python] require_serial: true - - id: isort - name: isort - entry: uv run isort + - id: ruff + name: ruff check + entry: uv run ruff check --fix language: system types: [python] require_serial: true @@ -28,11 +28,6 @@ repos: language: system types: [python] require_serial: true - - id: flake8 - name: flake8 - entry: uv run flake8 - language: system - types: [python] - id: pylint name: pylint entry: uv run pylint diff --git a/example/get_tokens.py b/example/get_tokens.py index e8451b6..a131c38 100644 --- a/example/get_tokens.py +++ b/example/get_tokens.py @@ -1,4 +1,4 @@ -"""Example to fetch local authentication tokens""" +"""Example to fetch local authentication tokens.""" import json from os import getenv diff --git a/glocaltokens/__init__.py b/glocaltokens/__init__.py index e69de29..a36b363 100644 --- a/glocaltokens/__init__.py +++ b/glocaltokens/__init__.py @@ -0,0 +1 @@ +"""Glocaltokens library.""" diff --git a/glocaltokens/client.py b/glocaltokens/client.py index 3150974..fddf76a 100644 --- a/glocaltokens/client.py +++ b/glocaltokens/client.py @@ -1,4 +1,4 @@ -"""Client""" +"""Client.""" from __future__ import annotations @@ -6,6 +6,7 @@ import json import logging import random +from typing import TYPE_CHECKING from ghome_foyer_api.api_pb2 import ( # pylint: disable=no-name-in-module GetHomeGraphRequest, @@ -14,7 +15,6 @@ from ghome_foyer_api.api_pb2_grpc import StructuresServiceStub from gpsoauth import perform_master_login, perform_oauth import grpc -from zeroconf import Zeroconf from .const import ( ACCESS_TOKEN_APP_NAME, @@ -28,17 +28,21 @@ HOMEGRAPH_DURATION, ) from .scanner import NetworkDevice, discover_devices -from .types import DeviceDict from .utils import network as net_utils, token as token_utils from .utils.logs import censor from .utils.network import is_valid_ipv4_address +if TYPE_CHECKING: + from zeroconf import Zeroconf + + from .types import DeviceDict + logging.basicConfig(level=logging.ERROR) LOGGER = logging.getLogger(__name__) class Device: - """Device representation""" + """Device representation.""" def __init__( self, @@ -48,9 +52,7 @@ def __init__( network_device: NetworkDevice | None = None, hardware: str | None = None, ): - """ - Initializes a Device. - """ + """Initialize a Device.""" log_prefix = f"[Device - {device_name}(id={device_id})]" LOGGER.debug("%s Initializing new Device instance", log_prefix) self.device_id = device_id @@ -109,10 +111,11 @@ def __init__( self.local_auth_token = local_auth_token def __str__(self) -> str: + """Return string representation of the device.""" return str(self.as_dict()) def as_dict(self) -> DeviceDict: - """Dictionary representation""" + """Return the dictionary representation of the device.""" return { "device_id": self.device_id, "device_name": self.device_name, @@ -126,7 +129,7 @@ def as_dict(self) -> DeviceDict: class GLocalAuthenticationTokens: - """Client""" + """Client.""" def __init__( self, @@ -136,18 +139,14 @@ def __init__( android_id: str | None = None, verbose: bool = False, ): - """ - Initialize an GLocalAuthenticationTokens instance with google account - credentials - :params - username: google account username; - password: google account password (can be an app password); - master_token: google master token (instead of username/password - combination); - android_id: the id of an android device. Will be randomly generated - if not set; - verbose: whether or not print debug logging information + """Initialize a GLocalAuthenticationTokens instance with Google account credentials. + :params + username: Google account username; + password: Google account password (can be an app password); + master_token: Google master token (instead of username/password combination); + android_id: The ID of an Android device. Will be randomly generated if not set; + verbose: Whether or not to print debug logging information. """ self.logging_level = logging.DEBUG if verbose else logging.ERROR LOGGER.setLevel(self.logging_level) @@ -190,7 +189,7 @@ def __init__( @staticmethod def _generate_android_id() -> str: - """Generate random 16 char long string""" + """Generate random 16 char long string.""" LOGGER.debug("Generating android id...") mac_string = "".join( [f"{random.randrange(16):x}" for _ in range(ANDROID_ID_LENGTH)] @@ -199,7 +198,7 @@ def _generate_android_id() -> str: return mac_string def get_android_id(self) -> str: - """Return existing or generate android id""" + """Return existing or generate android id.""" if not self.android_id: LOGGER.debug("There is no stored android_id, generating a new one") self.android_id = self._generate_android_id() @@ -207,16 +206,16 @@ def get_android_id(self) -> str: @staticmethod def _has_expired(creation_dt: datetime, duration: int) -> bool: - """Checks if an specified token/object has expired""" + """Check if an specified token/object has expired.""" return datetime.now().timestamp() - creation_dt.timestamp() > duration @staticmethod def _escape_username(username: str) -> str: - """Escape plus sign for some exotic accounts""" + """Escape plus sign for some exotic accounts.""" return username.replace("+", "%2B") def get_master_token(self) -> str | None: - """Get google master token from username and password""" + """Get google master token from username and password.""" if self.username is None or self.password is None: LOGGER.error("Username and password are not set.") return None @@ -234,7 +233,7 @@ def get_master_token(self) -> str | None: self.get_android_id(), ) except ValueError: - LOGGER.error( + LOGGER.exception( "A ValueError exception has been thrown, this usually is related" "to a password length that exceeds the boundaries (too long)." ) @@ -247,7 +246,7 @@ def get_master_token(self) -> str | None: return self.master_token def get_access_token(self) -> str | None: - """Return existing or fetch access_token""" + """Return existing or fetch access_token.""" if ( self.access_token is None or self.access_token_date is None @@ -286,7 +285,7 @@ def get_access_token(self) -> str | None: return self.access_token def get_homegraph(self, auth_attempts: int = 3) -> GetHomeGraphResponse | None: - """Returns the entire Google Home Foyer V2 service""" + """Return the entire Google Home Foyer V2 service.""" if ( self.homegraph is None or self.homegraph_date is None @@ -342,7 +341,7 @@ def get_homegraph(self, auth_attempts: int = 3) -> GetHomeGraphResponse | None: ) self.invalidate_access_token() return self.get_homegraph(auth_attempts - 1) - LOGGER.error( + LOGGER.exception( "%s Received unknown RPC error: code=%s message=%s", log_prefix, rpc_error.code(), # pylint: disable=no-member @@ -360,9 +359,7 @@ def get_google_devices( force_homegraph_reload: bool = False, discovery_timeout: int = DISCOVERY_TIMEOUT, ) -> list[Device]: - """ - Returns a list of google devices with their local authentication tokens, - and IP and ports if set in models_list. + """Return a list of Google devices with their local authentication tokens, IP, and ports. models_list: The list of accepted model names. disable_discovery: Whether or not the device's IP and port should @@ -397,11 +394,9 @@ def is_dict_with_valid_ipv4_addresses(data: dict[str, str]) -> bool: ) if addresses and not is_dict_with_valid_ipv4_addresses(addresses): - # We need to disable flake8-use-fstring because of the brackets, - # it causes a false positive. LOGGER.error( "Invalid dictionary structure for addresses dictionary " - "argument. Correct structure is {'device_name': 'ipaddress'}" # noqa + "argument. Correct structure is {'device_name': 'ipaddress'}" ) return devices @@ -492,12 +487,10 @@ def get_google_devices_json( zeroconf_instance: Zeroconf | None = None, force_homegraph_reload: bool = False, ) -> str: - """ - Returns a json list of google devices with their local authentication tokens, - and IP and ports if set in models_list. + """Return a JSON list of devices with authentication tokens, IPs, and ports if models set. models_list: The list of accepted model names. - indent: The indentation for the json formatting. + indent: The indentation for the JSON formatting. disable_discovery: Whether or not the device's IP and port should be searched for in the network. addresses: Dict of network devices from the local network @@ -515,24 +508,21 @@ def get_google_devices_json( zeroconf_instance=zeroconf_instance, force_homegraph_reload=force_homegraph_reload, ) - json_string = json.dumps( - [obj.as_dict() for obj in google_devices], indent=indent - ) - return json_string + return json.dumps([obj.as_dict() for obj in google_devices], indent=indent) def invalidate_access_token(self) -> None: - """Invalidates the current access token""" + """Invalidate the current access token.""" self.access_token = None self.access_token_date = None LOGGER.debug("Invalidated access_token") def invalidate_master_token(self) -> None: - """Invalidates the current master token""" + """Invalidate the current master token.""" self.master_token = None LOGGER.debug("Invalidated master_token") def invalidate_homegraph(self) -> None: - """Invalidates the stored homegraph data""" + """Invalidate the stored homegraph data.""" self.homegraph = None self.homegraph_date = None LOGGER.debug("Invalidated homegraph") diff --git a/glocaltokens/const.py b/glocaltokens/const.py index 7fbde8f..8072c15 100644 --- a/glocaltokens/const.py +++ b/glocaltokens/const.py @@ -1,4 +1,4 @@ -"""Globally used constants""" +"""Globally used constants.""" from __future__ import annotations diff --git a/glocaltokens/scanner.py b/glocaltokens/scanner.py index 8240121..dbecfe3 100644 --- a/glocaltokens/scanner.py +++ b/glocaltokens/scanner.py @@ -1,21 +1,24 @@ -"""Zeroconf based scanner""" +"""Zeroconf based scanner.""" from __future__ import annotations import logging from threading import Event -from typing import Callable, NamedTuple +from typing import TYPE_CHECKING, NamedTuple from zeroconf import ServiceBrowser, ServiceInfo, ServiceListener, Zeroconf from .const import DISCOVERY_TIMEOUT, GOOGLE_CAST_GROUP from .utils import network as net_utils +if TYPE_CHECKING: + from collections.abc import Callable + LOGGER = logging.getLogger(__name__) class NetworkDevice(NamedTuple): - """Discovered Google device representation""" + """Discovered Google device representation.""" name: str ip_address: str @@ -25,10 +28,10 @@ class NetworkDevice(NamedTuple): class CastListener(ServiceListener): - """ - Zeroconf Cast Services collection. + """Zeroconf Cast Services collection. + Credit (pychromecast): - https://github.com/home-assistant-libs/pychromecast/ + https://github.com/home-assistant-libs/pychromecast/. """ def __init__( @@ -37,6 +40,7 @@ def __init__( remove_callback: Callable[[], None] | None = None, update_callback: Callable[[], None] | None = None, ): + """Create cast listener.""" self.devices: dict[str, NetworkDevice] = {} self.add_callback = add_callback self.remove_callback = remove_callback @@ -58,7 +62,7 @@ def update_service(self, zc: Zeroconf, type_: str, name: str) -> None: self._add_update_service(zc, type_, name, self.update_callback) def remove_service(self, _zc: Zeroconf, type_: str, name: str) -> None: - """Called when a cast has been lost (mDNS info expired or host down).""" + """Remove a cast device when its mDNS info expires or the host is down.""" LOGGER.debug("remove_service %s, %s", type_, name) if name in self.devices: del self.devices[name] @@ -146,13 +150,13 @@ def discover_devices( zeroconf_instance: Zeroconf | None = None, logging_level: int = logging.ERROR, ) -> list[NetworkDevice]: - """Discover devices""" + """Discover devices.""" LOGGER.setLevel(logging_level) LOGGER.debug("Discovering devices...") def callback() -> None: - """Called when zeroconf has discovered a new device.""" + """Handle the event when zeroconf discovers a new device.""" if max_devices is not None and listener.count >= max_devices: discovery_complete.set() diff --git a/glocaltokens/types.py b/glocaltokens/types.py index 44d5a7b..dd49f8f 100644 --- a/glocaltokens/types.py +++ b/glocaltokens/types.py @@ -1,4 +1,4 @@ -"""Types used for package typing""" +"""Types used for package typing.""" from __future__ import annotations diff --git a/glocaltokens/utils/__init__.py b/glocaltokens/utils/__init__.py index e69de29..f9a8320 100644 --- a/glocaltokens/utils/__init__.py +++ b/glocaltokens/utils/__init__.py @@ -0,0 +1 @@ +"""Utils package.""" diff --git a/glocaltokens/utils/logs.py b/glocaltokens/utils/logs.py index 8a959cd..2785c21 100644 --- a/glocaltokens/utils/logs.py +++ b/glocaltokens/utils/logs.py @@ -1,4 +1,4 @@ -"""Log utilities""" +"""Log utilities.""" from __future__ import annotations @@ -6,8 +6,7 @@ def censor( text: str | None, hide_length: bool = False, hide_first_letter: bool = False ) -> str: - """ - Hide sensitive information. + """Hide sensitive information. text: The text to censure. """ diff --git a/glocaltokens/utils/network.py b/glocaltokens/utils/network.py index 9b22c8e..5a19846 100644 --- a/glocaltokens/utils/network.py +++ b/glocaltokens/utils/network.py @@ -1,4 +1,4 @@ -"""Network utilities""" +"""Network utilities.""" from __future__ import annotations @@ -6,7 +6,7 @@ def is_valid_ipv4_address(address: str) -> bool: - """Check if ip address is ipv4""" + """Check if ip address is ipv4.""" try: socket.inet_pton(socket.AF_INET, address) except AttributeError: # no inet_pton here, sorry @@ -22,7 +22,7 @@ def is_valid_ipv4_address(address: str) -> bool: def is_valid_ipv6_address(address: str) -> bool: - """Check if ip address is ipv6""" + """Check if ip address is ipv6.""" try: socket.inet_pton(socket.AF_INET6, address) except OSError: # not a valid address @@ -31,5 +31,5 @@ def is_valid_ipv6_address(address: str) -> bool: def is_valid_port(port: int) -> bool: - """Check if port is valid""" + """Check if port is valid.""" return 0 <= port <= 65535 diff --git a/glocaltokens/utils/token.py b/glocaltokens/utils/token.py index 7da6ef3..f862f8e 100644 --- a/glocaltokens/utils/token.py +++ b/glocaltokens/utils/token.py @@ -1,21 +1,21 @@ -"""Token utilities""" +"""Token utilities.""" import random import string def is_aas_et(token: str) -> bool: - """AAS type token""" + """AAS type token.""" return isinstance(token, str) and token.startswith("aas_et/") and len(token) == 223 def is_local_auth_token(token: str) -> bool: - """Local authentication token""" + """Local authentication token.""" return len(token) == 108 def generate(length: int, prefix: str = "", suffix: str = "") -> str: - """Generate token""" + """Generate token.""" return ( prefix + "".join(random.choice(string.ascii_letters) for x in range(length)) diff --git a/pyproject.toml b/pyproject.toml index 2af58b3..deb9aa1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,14 +23,14 @@ repository = "https://github.com/leikoilja/glocaltokens" requires-python = ">=3.9" dynamic = ["version"] dependencies = [ - "ghome-foyer-api >= 1.0.0", - "gpsoauth >= 1.1.1", - "simplejson >= 3.19.3", + "ghome-foyer-api>=1.0.0", + "gpsoauth>=1.1.1", + "simplejson>=3.19.3", # Note, we want to keep versions of grpcio, requests and zeroconf similar to Home Assistant # https://github.com/home-assistant/core/blob/2024.9.0/homeassistant/package_constraints.txt - "grpcio >= 1.59.0", - "requests >= 2.32.3", - "zeroconf >= 0.133.0", + "grpcio>=1.59.0", + "requests>=2.32.3", + "zeroconf>=0.133.0", ] [project.urls] @@ -39,22 +39,16 @@ dependencies = [ [tool.uv] dev-dependencies = [ - "codespell >= 2.3.0", - "faker >= 20.1.0", - "flake8 >= 6.1.0", - "flake8-bugbear >= 23.12.2", - "flake8-comprehensions >= 3.15.0", - "flake8-simplify >= 0.21.0", - "flake8-use-fstring >= 1.4", - "grpc-stubs >= 1.53.0.5", - "isort >= 5.13.2", - "mock >= 5.1.0", - "mypy >= 1.11.0", - "pre-commit >= 3.8.0", - "pylint >= 3.2.7", - "pytest >= 7.4.4", + "codespell>=2.3.0", + "faker>=20.1.0", + "grpc-stubs>=1.53.0.5", + "mock>=5.1.0", + "mypy>=1.11.0", + "pre-commit>=3.8.0", + "pylint>=3.2.7", + "pytest>=7.4.4", "ruff>=0.6.4", - "types-protobuf >= 4.25.0.20240417", + "types-protobuf>=4.25.0.20240417", ] [build-system] @@ -68,6 +62,123 @@ source = "vcs" required-version = ">=0.6.0" target-version = "py39" +[tool.ruff.lint] +select = [ + "A001", # Variable {name} is shadowing a Python builtin + "ASYNC", # flake8-async + "B", # flake8-bugbear + "BLE", + "C", # complexity, including flake8-comprehensions + "COM818", # Trailing comma on bare tuple prohibited + "D", # flake8-docstrings + "DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow() + "DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts) + "E", # pycodestyle + "F", # pyflakes/autoflake + "FLY", # flynt + "FURB", # refurb + "G", # flake8-logging-format + "I", # isort + "INP", # flake8-no-pep420 + "ISC", # flake8-implicit-str-concat + "ICN001", # import concentions; {name} should be imported as {asname} + "LOG", # flake8-logging + "N804", # First argument of a class method should be named cls + "N805", # First argument of a method should be named self + "N815", # Variable {name} in class scope should not be mixedCase + "PERF", # Perflint + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "PTH", # flake8-use-pathlib + "PYI", # flake8-pyi + "RET", # flake8-return + "RSE", # flake8-raise + "RUF005", # Consider iterable unpacking instead of concatenation + "RUF006", # Store a reference to the return value of asyncio.create_task + "RUF010", # Use explicit conversion flag + "RUF013", # PEP 484 prohibits implicit Optional + "RUF017", # Avoid quadratic list summation + "RUF018", # Avoid assignment expressions in assert statements + "RUF019", # Unnecessary key check before dictionary access + # "RUF100", # Unused `noqa` directive; temporarily every now and then to clean them up + "S102", # Use of exec detected + "S103", # bad-file-permissions + "S108", # hardcoded-temp-file + "S306", # suspicious-mktemp-usage + "S307", # suspicious-eval-usage + "S313", # suspicious-xmlc-element-tree-usage + "S314", # suspicious-xml-element-tree-usage + "S315", # suspicious-xml-expat-reader-usage + "S316", # suspicious-xml-expat-builder-usage + "S317", # suspicious-xml-sax-usage + "S318", # suspicious-xml-mini-dom-usage + "S319", # suspicious-xml-pull-dom-usage + "S320", # suspicious-xmle-tree-usage + "S601", # paramiko-call + "S602", # subprocess-popen-with-shell-equals-true + "S604", # call-with-shell-equals-true + "S608", # hardcoded-sql-expression + "S609", # unix-command-wildcard-injection + "SIM", # flake8-simplify + "SLF", # flake8-self + "SLOT", # flake8-slots + "T100", # Trace found: {name} used + "TCH", # flake8-type-checking + "TID251", # Banned imports + "TRY", # tryceratops + "UP", # pyupgrade + "W", # pycodestyle +] +ignore = [ + "D202", # No blank lines allowed after function docstring + "D203", # 1 blank line required before class docstring + "D213", # Multi-line docstring summary should start at the second line + "E501", # line too long + "PLR0911", # Too many return statements ({returns} > {max_returns}) + "PLR0912", # Too many branches ({branches} > {max_branches}) + "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) + "PLR0915", # Too many statements ({statements} > {max_statements}) + "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable + "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target + "TRY003", # Avoid specifying long messages outside the exception class + # May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules + "W191", + "E111", + "E114", + "E117", + "D206", + "D300", + "Q", + "COM812", + "COM819", + "ISC001", +] + +[tool.ruff.lint.per-file-ignores] +"tests/test_client.py" = ["SLF001"] +"example/*.py" = ["INP001"] + +[tool.ruff.lint.isort] +force-sort-within-sections = true +combine-as-imports = true +split-on-trailing-comma = false + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false + +[tool.ruff.lint.mccabe] +max-complexity = 25 + +[tool.pylint.main] +py-version = "3.9" +load-plugins = [ + "pylint.extensions.code_style", + "pylint.extensions.typing", +] + [tool.pylint.master] extension-pkg-whitelist = [ "_socket", @@ -79,25 +190,27 @@ good-names = [ ] [tool.pylint.format] -max-line-length = 88 +expected-line-ending-format = "LF" +max-line-length = 100 min-similarity-lines = 7 [tool.pylint.messages_control] # Reasons disabled: # too-many-* - are not enforced for the sake of readability # too-few-* - same as too-many-* +# --- +# Pylint CodeStyle plugin +# consider-using-namedtuple-or-dataclass - too opinionated +# consider-using-assignment-expr - decision to use := better left to devs disable = [ "too-few-public-methods", "too-many-arguments", "too-many-instance-attributes", "too-many-locals", + "consider-using-namedtuple-or-dataclass", + "consider-using-assignment-expr", ] -[tool.isort] -profile = "black" -force_sort_within_sections = true -combine_as_imports = true - [tool.mypy] python_version = "3.9" show_error_codes = true diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 350a3e5..0000000 --- a/setup.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[flake8] -doctests = True -# To work with Black -max-line-length = 88 -# E501: Line too long -# W503: Line break occurred before a binary operator -# E203: Whitespace before ':' -# D202: No blank lines allowed after function docstring -# W504: Line break after binary operator -# SIM119: Use a dataclass. It has many false-positives. -ignore = - E501, - W503, - E203, - D202, - W504, - SIM119 diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..46816dd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests package.""" diff --git a/tests/assertions.py b/tests/assertions.py index 838d429..60b342e 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -1,6 +1,4 @@ -""" -Common assertion helper classes used for unittesting -""" +"""Common assertion helper classes used for unittesting.""" # pylint: disable=invalid-name from unittest import TestCase @@ -14,38 +12,33 @@ class DeviceAssertions(TestCase): - """Device specific assessors""" + """Device specific assessors.""" def assertDevice( self, homegraph_device: Device, homegraph_device_struct: GetHomeGraphResponse.Home.Device, ) -> None: + """Assert a Device class object is created for each received homegraph device. + + In testing, the homegraph devices that are created are of type Struct. """ - Custom assertion because we create Device class object - for each of received homegraph devices, - while in testing homegraph devices that we create are of type Struct - """ - self.assertEqual( - homegraph_device.local_auth_token, homegraph_device_struct.local_auth_token - ) - self.assertEqual( - homegraph_device.device_id, homegraph_device_struct.device_info.device_id + assert ( + homegraph_device.local_auth_token + == homegraph_device_struct.local_auth_token ) - self.assertEqual( - homegraph_device.device_name, homegraph_device_struct.device_name - ) - self.assertEqual( - homegraph_device.hardware, homegraph_device_struct.hardware.model + assert ( + homegraph_device.device_id == homegraph_device_struct.device_info.device_id ) + assert homegraph_device.device_name == homegraph_device_struct.device_name + assert homegraph_device.hardware == homegraph_device_struct.hardware.model class TypeAssertions(TestCase): - """Type assessors""" + """Type assessors.""" def assertIsAasEt(self, variable: str) -> None: - """Assert the given variable is a of string type and follows AAS token format""" - self.assertTrue( - token_utils.is_aas_et(variable), - msg=f"Given variable {variable} doesn't follow the AAS_ET format", - ) + """Assert the given variable follows AAS token format.""" + assert token_utils.is_aas_et( + variable + ), f"Given variable {variable} doesn't follow the AAS_ET format" diff --git a/tests/factory/__init__.py b/tests/factory/__init__.py index e69de29..3dcc6b1 100644 --- a/tests/factory/__init__.py +++ b/tests/factory/__init__.py @@ -0,0 +1 @@ +"""Providers for tests.""" diff --git a/tests/factory/providers.py b/tests/factory/providers.py index 10b6f55..519ab14 100644 --- a/tests/factory/providers.py +++ b/tests/factory/providers.py @@ -1,4 +1,4 @@ -"""Test factory providers""" +"""Test factory providers.""" from __future__ import annotations @@ -19,36 +19,36 @@ class UtilsProvider(BaseProvider): - """Utility provider""" + """Utility provider.""" def version(self) -> str: - """Generates random version""" + """Generate random version.""" return f"{faker.pyint()}.{faker.pyint()}.{faker.pyint()}" class TokenProvider(BaseProvider): - """Token provider""" + """Token provider.""" def master_token(self) -> str: - """Generates random master token""" + """Generate random master token.""" return generate_token(MASTER_TOKEN_LENGTH, prefix="aas_et/") def access_token(self) -> str: - """Generates random access token""" + """Generate random access token.""" return generate_token(ACCESS_TOKEN_LENGTH, prefix="ya29.") def local_auth_token(self) -> str: - """Generates random local_auth_token token""" + """Generate random local_auth_token token.""" return generate_token(LOCAL_AUTH_TOKEN_LENGTH) class HomegraphProvider(TokenProvider): - """Homegraph provider""" + """Homegraph provider.""" def homegraph_device( self, device_name: str | None = None ) -> GetHomeGraphResponse.Home.Device: - """Using the content from test requests as reference""" + """Create and return a mocked HomeGraph device object.""" device_name = device_name if device_name else faker.word() return GetHomeGraphResponse.Home.Device( @@ -63,8 +63,7 @@ def homegraph_device( def homegraph_devices( self, min_devices: int = 1, max_devices: int = 10, count: int | None = None ) -> list[GetHomeGraphResponse.Home.Device]: - """ - Generates a random amount of devices, in the range specified. + """Generate a random amount of devices, in the range specified. min_devices: The number minimum of devices to generate. Should be greater than 0 diff --git a/tests/test_client.py b/tests/test_client.py index 9e40203..43ab3e8 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,4 +1,4 @@ -"""Client specific unittests""" +"""Client specific unittests.""" from __future__ import annotations @@ -38,16 +38,16 @@ class GLocalAuthenticationTokensClientTests(DeviceAssertions, TypeAssertions, TestCase): - """GLocalAuthenticationTokens clien specific unittests""" + """GLocalAuthenticationTokens clien specific unittests.""" def setUp(self) -> None: - """Setup method run before every test""" + """Set up the test client before each test.""" self.client = GLocalAuthenticationTokens( username=faker.word(), password=faker.word() ) def test_initialization(self) -> None: - """Valid initialization tests""" + """Valid initialization tests.""" username = faker.word() password = faker.word() master_token = faker.master_token() @@ -59,35 +59,35 @@ def test_initialization(self) -> None: master_token=master_token, android_id=android_id, ) - self.assertEqual(username, client.username) - self.assertEqual(password, client.password) - self.assertEqual(master_token, client.master_token) - self.assertEqual(android_id, client.android_id) + assert username == client.username + assert password == client.password + assert master_token == client.master_token + assert android_id == client.android_id - self.assertIsNone(client.access_token) - self.assertIsNone(client.homegraph) - self.assertIsNone(client.access_token_date) - self.assertIsNone(client.homegraph_date) + assert client.access_token is None + assert client.homegraph is None + assert client.access_token_date is None + assert client.homegraph_date is None assert client.master_token is not None self.assertIsAasEt(client.master_token) @patch("glocaltokens.client.LOGGER.error") def test_initialization__valid(self, m_log: NonCallableMock) -> None: - """Valid initialization tests""" + """Valid initialization tests.""" # With username and password GLocalAuthenticationTokens(username=faker.word(), password=faker.word()) - self.assertEqual(m_log.call_count, 0) + assert m_log.call_count == 0 # With master_token GLocalAuthenticationTokens(master_token=faker.master_token()) - self.assertEqual(m_log.call_count, 0) + assert m_log.call_count == 0 @patch("glocaltokens.client.LOGGER.setLevel") def test_initialization__valid_verbose_logger( self, m_set_level: NonCallableMock ) -> None: - """Valid initialization tests with verbose logging""" + """Valid initialization tests with verbose logging.""" # Non verbose GLocalAuthenticationTokens(username=faker.word(), password=faker.word()) m_set_level.assert_called_once_with(logging.ERROR) @@ -102,57 +102,53 @@ def test_initialization__valid_verbose_logger( @patch("glocaltokens.client.LOGGER.error") def test_initialization__invalid(self, m_log: NonCallableMock) -> None: - """Invalid initialization tests""" + """Invalid initialization tests.""" # Without username GLocalAuthenticationTokens(password=faker.word()) - self.assertEqual(m_log.call_count, 1) + assert m_log.call_count == 1 # Without password GLocalAuthenticationTokens(username=faker.word()) - self.assertEqual(m_log.call_count, 2) + assert m_log.call_count == 2 # Without username and password GLocalAuthenticationTokens() - self.assertEqual(m_log.call_count, 3) + assert m_log.call_count == 3 # With invalid master_token GLocalAuthenticationTokens(master_token=faker.word()) - self.assertEqual(m_log.call_count, 4) + assert m_log.call_count == 4 def test_get_android_id(self) -> None: - """Test getting android id""" + """Test getting android id.""" android_id = self.client.get_android_id() - self.assertTrue(len(android_id) == ANDROID_ID_LENGTH) + assert len(android_id) == ANDROID_ID_LENGTH # Make sure we get the same ID when called further - self.assertEqual(android_id, self.client.get_android_id()) + assert android_id == self.client.get_android_id() # pylint: disable=protected-access def test_generate_android_id(self) -> None: - """Test generating mac string""" + """Test generating mac string.""" android_id = GLocalAuthenticationTokens._generate_android_id() - self.assertTrue(len(android_id) == ANDROID_ID_LENGTH) + assert len(android_id) == ANDROID_ID_LENGTH # Make sure we get different generated mac string - self.assertNotEqual( - android_id, GLocalAuthenticationTokens._generate_android_id() - ) + assert android_id != GLocalAuthenticationTokens._generate_android_id() def test_has_expired(self) -> None: - """Test expiry method""" + """Test expiry method.""" duration_sec = 60 now = datetime.now() token_dt_expired = now - timedelta(seconds=duration_sec + 1) token_dt_non_expired = now - timedelta(seconds=duration_sec - 1) # Expired - self.assertTrue( - GLocalAuthenticationTokens._has_expired(token_dt_expired, duration_sec) - ) + assert GLocalAuthenticationTokens._has_expired(token_dt_expired, duration_sec) # Non expired - self.assertFalse( - GLocalAuthenticationTokens._has_expired(token_dt_non_expired, duration_sec) + assert not GLocalAuthenticationTokens._has_expired( + token_dt_non_expired, duration_sec ) @patch("glocaltokens.client.LOGGER.error") @@ -160,13 +156,13 @@ def test_has_expired(self) -> None: def test_get_master_token( self, m_perform_master_login: NonCallableMock, m_log: NonCallableMock ) -> None: - """Test getting master token""" + """Test getting master token.""" # No token in response - self.assertIsNone(self.client.get_master_token()) + assert self.client.get_master_token() is None m_perform_master_login.assert_called_once_with( self.client.username, self.client.password, self.client.get_android_id() ) - self.assertEqual(m_log.call_count, 1) + assert m_log.call_count == 1 # Reset mocks m_perform_master_login.reset_mock() @@ -179,12 +175,12 @@ def test_get_master_token( m_perform_master_login.assert_called_once_with( self.client.username, self.client.password, self.client.get_android_id() ) - self.assertEqual(expected_master_token, master_token) - self.assertEqual(m_log.call_count, 0) + assert expected_master_token == master_token + assert m_log.call_count == 0 # Another request - must return the same token all the time master_token = self.client.get_master_token() - self.assertEqual(expected_master_token, master_token) + assert expected_master_token == master_token # Reset mocks m_perform_master_login.reset_mock() @@ -194,8 +190,8 @@ def test_get_master_token( m_perform_master_login.side_effect = ValueError("Plaintext is too long.") self.client.invalidate_master_token() master_token = self.client.get_master_token() - self.assertIsNone(master_token) - self.assertEqual(m_log.call_count, 2) + assert master_token is None + assert m_log.call_count == 2 @patch("glocaltokens.client.LOGGER.error") @patch("glocaltokens.client.perform_master_login") @@ -206,12 +202,12 @@ def test_get_access_token( m_get_master_token: NonCallableMock, m_log: NonCallableMock, ) -> None: - """Test getting access token""" + """Test getting access token.""" master_token = faker.master_token() m_get_master_token.return_value = {"Token": master_token} # No token in response - self.assertIsNone(self.client.get_access_token()) + assert self.client.get_access_token() is None m_perform_oauth.assert_called_once_with( self.client.username, master_token, @@ -220,7 +216,7 @@ def test_get_access_token( service=ACCESS_TOKEN_SERVICE, client_sig=ACCESS_TOKEN_CLIENT_SIGNATURE, ) - self.assertEqual(m_log.call_count, 1) + assert m_log.call_count == 1 # Reset mocks m_perform_oauth.reset_mock() @@ -238,8 +234,8 @@ def test_get_access_token( service=ACCESS_TOKEN_SERVICE, client_sig=ACCESS_TOKEN_CLIENT_SIGNATURE, ) - self.assertEqual(expected_access_token, access_token) - self.assertEqual(m_log.call_count, 0) + assert expected_access_token == access_token + assert m_log.call_count == 0 # Reset mocks m_perform_oauth.reset_mock() @@ -248,8 +244,8 @@ def test_get_access_token( # Another request with non expired token must return the same token # (no new requests) access_token = self.client.get_access_token() - self.assertEqual(expected_access_token, access_token) - self.assertEqual(m_perform_oauth.call_count, 0) + assert expected_access_token == access_token + assert m_perform_oauth.call_count == 0 # Another request with expired token must return new token (new request) assert self.client.access_token_date is not None @@ -257,7 +253,7 @@ def test_get_access_token( ACCESS_TOKEN_DURATION + 1 ) access_token = self.client.get_access_token() - self.assertEqual(m_perform_oauth.call_count, 1) + assert m_perform_oauth.call_count == 1 @patch("glocaltokens.client.grpc.ssl_channel_credentials") @patch("glocaltokens.client.grpc.access_token_call_credentials") @@ -268,7 +264,7 @@ def test_get_access_token( @patch("glocaltokens.client.GLocalAuthenticationTokens.get_access_token") def test_get_homegraph( self, - _m_get_access_token: NonCallableMock, + m_get_access_token: NonCallableMock, m_get_home_graph_request: NonCallableMock, m_structure_service_stub: NonCallableMock, m_secure_channel: NonCallableMock, @@ -276,25 +272,27 @@ def test_get_homegraph( m_access_token_call_credentials: NonCallableMock, m_ssl_channel_credentials: NonCallableMock, ) -> None: - """Test getting homegraph""" + """Test getting homegraph.""" # New homegraph self.client.get_homegraph() - self.assertEqual(m_ssl_channel_credentials.call_count, 1) - self.assertEqual(m_access_token_call_credentials.call_count, 1) - self.assertEqual(m_composite_channel_credentials.call_count, 1) - self.assertEqual(m_secure_channel.call_count, 1) - self.assertEqual(m_structure_service_stub.call_count, 1) - self.assertEqual(m_get_home_graph_request.call_count, 1) + assert m_ssl_channel_credentials.call_count == 1 + assert m_access_token_call_credentials.call_count == 1 + assert m_composite_channel_credentials.call_count == 1 + assert m_secure_channel.call_count == 1 + assert m_structure_service_stub.call_count == 1 + assert m_get_home_graph_request.call_count == 1 + assert m_get_access_token.call_count == 1 # Another request with non expired homegraph must return the same homegraph # (no new requests) self.client.get_homegraph() - self.assertEqual(m_ssl_channel_credentials.call_count, 1) - self.assertEqual(m_access_token_call_credentials.call_count, 1) - self.assertEqual(m_composite_channel_credentials.call_count, 1) - self.assertEqual(m_secure_channel.call_count, 1) - self.assertEqual(m_structure_service_stub.call_count, 1) - self.assertEqual(m_get_home_graph_request.call_count, 1) + assert m_ssl_channel_credentials.call_count == 1 + assert m_access_token_call_credentials.call_count == 1 + assert m_composite_channel_credentials.call_count == 1 + assert m_secure_channel.call_count == 1 + assert m_structure_service_stub.call_count == 1 + assert m_get_home_graph_request.call_count == 1 + assert m_get_access_token.call_count == 1 # Expired homegraph assert self.client.homegraph_date is not None @@ -302,12 +300,13 @@ def test_get_homegraph( HOMEGRAPH_DURATION + 1 ) self.client.get_homegraph() - self.assertEqual(m_ssl_channel_credentials.call_count, 2) - self.assertEqual(m_access_token_call_credentials.call_count, 2) - self.assertEqual(m_composite_channel_credentials.call_count, 2) - self.assertEqual(m_secure_channel.call_count, 2) - self.assertEqual(m_structure_service_stub.call_count, 2) - self.assertEqual(m_get_home_graph_request.call_count, 2) + assert m_ssl_channel_credentials.call_count == 2 + assert m_access_token_call_credentials.call_count == 2 + assert m_composite_channel_credentials.call_count == 2 + assert m_secure_channel.call_count == 2 + assert m_structure_service_stub.call_count == 2 + assert m_get_home_graph_request.call_count == 2 + assert m_get_access_token.call_count == 2 @patch("glocaltokens.client.GLocalAuthenticationTokens.get_access_token") @patch("glocaltokens.client.StructuresServiceStub") @@ -316,7 +315,7 @@ def test_get_homegraph_retries( m_structure_service_stub: NonCallableMock, m_get_access_token: NonCallableMock, ) -> None: - """Test retries in get_homegraph""" + """Test retries in get_homegraph.""" m_get_access_token.return_value = faker.word() rpc_error = grpc.RpcError() rpc_error.code = mock.Mock() # type: ignore[method-assign] @@ -324,14 +323,12 @@ def test_get_homegraph_retries( rpc_error.details = mock.Mock() # type: ignore[method-assign] m_structure_service_stub.return_value.GetHomeGraph.side_effect = rpc_error result = self.client.get_homegraph() - self.assertEqual(result, None) - self.assertEqual( - m_structure_service_stub.return_value.GetHomeGraph.call_count, 3 - ) + assert result is None + assert m_structure_service_stub.return_value.GetHomeGraph.call_count == 3 @patch("glocaltokens.client.GLocalAuthenticationTokens.get_homegraph") def test_get_google_devices(self, m_get_homegraph: NonCallableMock) -> None: - """Test getting google devices""" + """Test getting google devices.""" # With just one device returned from homegraph fake_device_name = faker.word() fake_ip_address = faker.ipv4() @@ -343,13 +340,13 @@ def test_get_google_devices(self, m_get_homegraph: NonCallableMock) -> None: disable_discovery=True, addresses={fake_device_name: fake_ip_address}, ) - self.assertEqual(len(google_devices), 1) + assert len(google_devices) == 1 google_device = google_devices[0] self.assertDevice(google_device, homegraph_device) - self.assertIsNotNone(google_device.network_device) + assert google_device.network_device is not None if google_device.network_device is not None: - self.assertEqual(google_device.network_device.ip_address, fake_ip_address) + assert google_device.network_device.ip_address == fake_ip_address # With two devices returned from homegraph # but one device having the invalid token @@ -368,19 +365,17 @@ def test_get_google_devices(self, m_get_homegraph: NonCallableMock) -> None: disable_discovery=True, addresses={fake_device_name: fake_ip_address}, ) - self.assertEqual(len(google_devices), 1) + assert len(google_devices) == 1 self.assertDevice(google_devices[0], homegraph_device_valid) - self.assertIsNotNone(google_devices[0].network_device) + assert google_devices[0].network_device is not None if google_devices[0].network_device is not None: - self.assertEqual( - google_devices[0].network_device.ip_address, fake_ip_address - ) + assert google_devices[0].network_device.ip_address == fake_ip_address @patch("glocaltokens.client.GLocalAuthenticationTokens.get_google_devices") def test_get_google_devices_json( self, m_get_google_devices: NonCallableMock ) -> None: - """Test getting google devices as JSON""" + """Test getting google devices as JSON.""" device_id = str(faker.uuid4()) device_name = faker.word() local_auth_token = faker.local_auth_token() @@ -400,36 +395,32 @@ def test_get_google_devices_json( m_get_google_devices.return_value = [google_device] json_string = self.client.get_google_devices_json(disable_discovery=True) - self.assertEqual(m_get_google_devices.call_count, 1) + assert m_get_google_devices.call_count == 1 received_json = json.loads(json_string) received_device = received_json[0] - self.assertEqual(received_device[JSON_KEY_DEVICE_NAME], device_name) - self.assertEqual(received_device[JSON_KEY_HARDWARE], hardware) - self.assertEqual(received_device[JSON_KEY_LOCAL_AUTH_TOKEN], local_auth_token) - self.assertEqual(received_device[JSON_KEY_NETWORK_DEVICE][JSON_KEY_PORT], port) - self.assertEqual( - received_device[JSON_KEY_NETWORK_DEVICE][JSON_KEY_IP], ip_address - ) + assert received_device[JSON_KEY_DEVICE_NAME] == device_name + assert received_device[JSON_KEY_HARDWARE] == hardware + assert received_device[JSON_KEY_LOCAL_AUTH_TOKEN] == local_auth_token + assert received_device[JSON_KEY_NETWORK_DEVICE][JSON_KEY_PORT] == port + assert received_device[JSON_KEY_NETWORK_DEVICE][JSON_KEY_IP] == ip_address def test_username_escaping(self) -> None: """Test that plus sign is escaped.""" - self.assertEqual( - self.client._escape_username("login@domain.com"), "login@domain.com" - ) - self.assertEqual( - self.client._escape_username("login+tag@domain.com"), - "login%2Btag@domain.com", + assert self.client._escape_username("login@domain.com") == "login@domain.com" + assert ( + self.client._escape_username("login+tag@domain.com") + == "login%2Btag@domain.com" ) - self.assertEqual(self.client._escape_username("login"), "login") + assert self.client._escape_username("login") == "login" # Such account should be impossible to create. - self.assertEqual(self.client._escape_username("login+tag"), "login%2Btag") + assert self.client._escape_username("login+tag") == "login%2Btag" class DeviceClientTests(TypeAssertions, TestCase): - """Device specific unittests""" + """Device specific unittests.""" def test_initialization__valid(self) -> None: - """Test initialization that is valid""" + """Test initialization that is valid.""" local_auth_token = faker.local_auth_token() # With ip and port @@ -446,11 +437,11 @@ def test_initialization__valid(self) -> None: local_auth_token=local_auth_token, ) - self.assertEqual(device.local_auth_token, local_auth_token) + assert device.local_auth_token == local_auth_token @patch("glocaltokens.client.LOGGER.warning") def test_initialization__invalid(self, m_log: NonCallableMock) -> None: - """Test initialization that is invalid""" + """Test initialization that is invalid.""" # Invalid local_auth_token device = Device( @@ -458,5 +449,5 @@ def test_initialization__invalid(self, m_log: NonCallableMock) -> None: device_name=faker.word(), local_auth_token=faker.word(), ) - self.assertEqual(m_log.call_count, 1) - self.assertIsNone(device.local_auth_token) + assert m_log.call_count == 1 + assert device.local_auth_token is None diff --git a/tests/test_scanner.py b/tests/test_scanner.py index ffb856c..0b4963f 100644 --- a/tests/test_scanner.py +++ b/tests/test_scanner.py @@ -1,4 +1,4 @@ -"""Scanner specific tests""" +"""Scanner specific tests.""" from __future__ import annotations @@ -16,12 +16,10 @@ class NetworkDeviceTests(TestCase): - """ - NetworkDevice specific tests - """ + """NetworkDevice specific tests.""" def test_initialization(self) -> None: - """Initialization tests""" + """Initialization tests.""" name = faker.word() ip_address = faker.ipv4_private() port = faker.port_number() @@ -29,21 +27,20 @@ def test_initialization(self) -> None: unique_id = faker.word() device = NetworkDevice(name, ip_address, port, model, unique_id) - self.assertEqual(name, device.name) - self.assertEqual(ip_address, device.ip_address) - self.assertEqual(port, device.port) - self.assertEqual(model, device.model) - self.assertEqual(unique_id, device.unique_id) + assert name == device.name + assert ip_address == device.ip_address + assert port == device.port + assert model == device.model + assert unique_id == device.unique_id - self.assertEqual( + assert ( f"NetworkDevice(name='{name}', ip_address='{ip_address}', " - f"port={port}, model='{model}', unique_id='{unique_id}')", - str(device), + f"port={port}, model='{model}', unique_id='{unique_id}')" == str(device) ) @patch("glocaltokens.scanner.LOGGER.error") def test_service_info__valid(self, m_error: NonCallableMock) -> None: - """Valid service_info tests""" + """Valid service_info tests.""" service = mock.Mock(name="Service") service.parsed_addresses.return_value = None service.server = faker.ipv4_private() @@ -56,11 +53,11 @@ def test_service_info__valid(self, m_error: NonCallableMock) -> None: type_ = faker.word() name = faker.word() listener.add_service(zc, type_, name) - self.assertEqual(m_error.call_count, 0) + assert m_error.call_count == 0 @patch("glocaltokens.scanner.LOGGER.error") def test_service_info__invalid(self, m_error: NonCallableMock) -> None: - """Invalid service_info tests""" + """Invalid service_info tests.""" service = mock.Mock(name="Service") service.parsed_addresses.return_value = None @@ -75,19 +72,19 @@ def test_service_info__invalid(self, m_error: NonCallableMock) -> None: service.server = faker.word() service.port = faker.port_number() listener.add_service(zc, type_, name) - self.assertEqual(m_error.call_count, 1) + assert m_error.call_count == 1 # With negative port service.server = faker.ipv4_private() service.port = faker.pyint(min_value=-9999, max_value=-1) listener.add_service(zc, type_, name) - self.assertEqual(m_error.call_count, 2) + assert m_error.call_count == 2 # With greater port service.server = faker.ipv4_private() service.port = faker.pyint(min_value=65535, max_value=999999) listener.add_service(zc, type_, name) - self.assertEqual(m_error.call_count, 3) + assert m_error.call_count == 3 # No devices should be added - self.assertEqual(listener.count, 0) + assert listener.count == 0 diff --git a/tests/test_utils.py b/tests/test_utils.py index 6bcfcce..f1d80cf 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,4 @@ -"""Utility tests""" +"""Utility tests.""" from unittest import TestCase @@ -10,30 +10,26 @@ class UtilsTests(TestCase): - """Utilities tests""" + """Utilities tests.""" def test_censor(self) -> None: - """Testing sensitive info censoring""" + """Testing sensitive info censoring.""" # With word secret_string = faker.word() censored_string = censor(secret_string) - self.assertNotEqual(secret_string, censored_string) - self.assertTrue(censored_string.startswith(secret_string[0])) - self.assertEqual( - censored_string, f"{secret_string[0]}{(len(secret_string) - 1) * '*'}" - ) + assert secret_string != censored_string + assert censored_string.startswith(secret_string[0]) + assert censored_string == f"{secret_string[0]}{(len(secret_string) - 1) * '*'}" # With empty string censored_string = censor("") - self.assertEqual(censored_string, "") + assert censored_string == "" # Hide first letter - self.assertEqual(censor("abc", hide_first_letter=True), "***") + assert censor("abc", hide_first_letter=True) == "***" # Hide length - self.assertEqual(censor("abc", hide_length=True), "a") + assert censor("abc", hide_length=True) == "a" # Hide both - self.assertEqual( - censor("abc", hide_first_letter=True, hide_length=True), "" - ) + assert censor("abc", hide_first_letter=True, hide_length=True) == "" diff --git a/uv.lock b/uv.lock index 964d34d..c045f1e 100644 --- a/uv.lock +++ b/uv.lock @@ -6,15 +6,6 @@ resolution-markers = [ "python_full_version >= '3.12'", ] -[[package]] -name = "astor" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/21/75b771132fee241dfe601d39ade629548a9626d1d39f333fde31bc46febe/astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e", size = 35090 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/88/97eef84f48fa04fbd6750e62dcceafba6c63c81b7ac1420856c8dcc0a3f9/astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5", size = 27488 }, -] - [[package]] name = "astroid" version = "3.2.4" @@ -36,15 +27,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028", size = 5721 }, ] -[[package]] -name = "attrs" -version = "24.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, -] - [[package]] name = "certifi" version = "2024.8.30" @@ -198,67 +180,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2f/95/f9310f35376024e1086c59cbb438d319fc9a4ef853289ce7c661539edbd4/filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609", size = 16170 }, ] -[[package]] -name = "flake8" -version = "7.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mccabe" }, - { name = "pycodestyle" }, - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/37/72/e8d66150c4fcace3c0a450466aa3480506ba2cae7b61e100a2613afc3907/flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38", size = 48054 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/42/65004373ac4617464f35ed15931b30d764f53cdd30cc78d5aea349c8c050/flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213", size = 57731 }, -] - -[[package]] -name = "flake8-bugbear" -version = "24.8.19" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "flake8" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/b3/80043856ed90ceef0e95c01ed12d6bb7d4d76356e526450f0a9aeeb0a7b7/flake8_bugbear-24.8.19.tar.gz", hash = "sha256:9b77627eceda28c51c27af94560a72b5b2c97c016651bdce45d8f56c180d2d32", size = 78771 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/3c/8885a79ae7bebfab19716969de9810722ff45354e4ccc974a090bc478eaa/flake8_bugbear-24.8.19-py3-none-any.whl", hash = "sha256:25bc3867f7338ee3b3e0916bf8b8a0b743f53a9a5175782ddc4325ed4f386b89", size = 35357 }, -] - -[[package]] -name = "flake8-comprehensions" -version = "3.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/fa/68481f25fc8ecdbe8a763062ba4f5b17fa4ef7fc0646c081267cef4f67e5/flake8_comprehensions-3.15.0.tar.gz", hash = "sha256:923c22603e0310376a6b55b03efebdc09753c69f2d977755cba8bb73458a5d4d", size = 13079 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/aa/93667d6f398749d1a9dd37d646e092f9f1baade7cbac948331b50a1d513c/flake8_comprehensions-3.15.0-py3-none-any.whl", hash = "sha256:b7e027bbb52be2ceb779ee12484cdeef52b0ad3c1fcb8846292bdb86d3034681", size = 8162 }, -] - -[[package]] -name = "flake8-simplify" -version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "astor" }, - { name = "flake8" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/c0/4cd41ef29b5b19da0c87c1de6f7cbae3b238b991e0e12f2d65bf29eea34f/flake8_simplify-0.21.0.tar.gz", hash = "sha256:c95ff1dcc1de5949af47e0087cbf1164445881131b15bcd7a71252670f492f4d", size = 34055 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/c5/832d27af89939d06b43cf36138ffaf27e5b38e0d2b82183ef46110ebeb96/flake8_simplify-0.21.0-py3-none-any.whl", hash = "sha256:439391e762a9370b371208add0b5c5c40c3d25a98e1f5421d263215d08194183", size = 26884 }, -] - -[[package]] -name = "flake8-use-fstring" -version = "1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/40/ec0437e5ec5b107df0b6ee967b60e6f575a4d38c8b0639d78c555987fa39/flake8-use-fstring-1.4.tar.gz", hash = "sha256:6550bf722585eb97dffa8343b0f1c372101f5c4ab5b07ebf0edd1c79880cdd39", size = 5751 } - [[package]] name = "ghome-foyer-api" version = "1.0.0" @@ -274,7 +195,7 @@ wheels = [ [[package]] name = "glocaltokens" -version = "0.7.2.dev25+ge83580e.d20240913" +version = "0.7.2.dev27+gce652f9.d20240913" source = { editable = "." } dependencies = [ { name = "ghome-foyer-api" }, @@ -289,13 +210,7 @@ dependencies = [ dev = [ { name = "codespell" }, { name = "faker" }, - { name = "flake8" }, - { name = "flake8-bugbear" }, - { name = "flake8-comprehensions" }, - { name = "flake8-simplify" }, - { name = "flake8-use-fstring" }, { name = "grpc-stubs" }, - { name = "isort" }, { name = "mock" }, { name = "mypy" }, { name = "pre-commit" }, @@ -319,13 +234,7 @@ requires-dist = [ dev = [ { name = "codespell", specifier = ">=2.3.0" }, { name = "faker", specifier = ">=20.1.0" }, - { name = "flake8", specifier = ">=6.1.0" }, - { name = "flake8-bugbear", specifier = ">=23.12.2" }, - { name = "flake8-comprehensions", specifier = ">=3.15.0" }, - { name = "flake8-simplify", specifier = ">=0.21.0" }, - { name = "flake8-use-fstring", specifier = ">=1.4" }, { name = "grpc-stubs", specifier = ">=1.53.0.5" }, - { name = "isort", specifier = ">=5.13.2" }, { name = "mock", specifier = ">=5.1.0" }, { name = "mypy", specifier = ">=1.11.0" }, { name = "pre-commit", specifier = ">=3.8.0" }, @@ -579,15 +488,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b5/95/0ba7f66934a0a798006f06fc3d74816da2b7a2bcfd9b98c53d26f684c89e/protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978", size = 156464 }, ] -[[package]] -name = "pycodestyle" -version = "2.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/aa/210b2c9aedd8c1cbeea31a50e42050ad56187754b34eb214c46709445801/pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521", size = 39232 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/d8/a211b3f85e99a0daa2ddec96c949cac6824bd305b040571b82a03dd62636/pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", size = 31284 }, -] - [[package]] name = "pycryptodomex" version = "3.20.0" @@ -616,15 +516,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4a/7b/881c25d94c821a83eedfa1a9193ff32f3436853f2daf7ce91a0d3d1aa74c/pycryptodomex-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:69138068268127cd605e03438312d8f271135a33140e2742b417d027a0539427", size = 1744358 }, ] -[[package]] -name = "pyflakes" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/f9/669d8c9c86613c9d568757c7f5824bd3197d7b1c6c27553bc5618a27cce2/pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", size = 63788 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725 }, -] - [[package]] name = "pylint" version = "3.2.7"