diff --git a/IndySDKtoAskarMigration.md b/IndySDKtoAskarMigration.md new file mode 100644 index 0000000000..9aa6ad4422 --- /dev/null +++ b/IndySDKtoAskarMigration.md @@ -0,0 +1 @@ +# Migrating from Indy SDK to Askar diff --git a/aries_cloudagent/config/banner.py b/aries_cloudagent/config/banner.py index 0598b6d54a..79d1f38278 100644 --- a/aries_cloudagent/config/banner.py +++ b/aries_cloudagent/config/banner.py @@ -1,10 +1,33 @@ """Module to contain logic to generate the banner for ACA-py.""" -class Banner: +from contextlib import contextmanager +from enum import Enum, auto +import sys +import textwrap +from typing import Optional, TextIO + + +@contextmanager +def Banner(border: str, length: int, file: Optional[TextIO] = None): + """Context manager to generate a banner for ACA-py.""" + banner = _Banner(border, length, file) + banner.print_border() + yield banner + banner.print_border() + + +class _Banner: """Management class to generate a banner for ACA-py.""" - def __init__(self, border: str, length: int): + class align(Enum): + """Alignment options for banner elements.""" + + LEFT = auto() + CENTER = auto() + RIGHT = auto() + + def __init__(self, border: str, length: int, file: Optional[TextIO] = None): """Initialize the banner object. The ``border`` is a single character to be used, and ``length`` @@ -12,43 +35,94 @@ def __init__(self, border: str, length: int): """ self.border = border self.length = length + self.file = file or sys.stdout + + def _print(self, text: str): + """Print value.""" + print(text, file=self.file) + + def _lr_pad(self, content: str): + """Pad string content with defined border character. + + Args: + content: String content to pad + """ + return f"{self.border}{self.border} {content} {self.border}{self.border}" + + def _print_line(self, text: str, alignment: align = align.LEFT): + """Print line.""" + lines = textwrap.wrap(text, width=self.length) + for line in lines: + if len(line) < self.length: + if alignment == self.align.LEFT: + left = "" + right = " " * (self.length - len(line)) + elif alignment == self.align.CENTER: + left = " " * ((self.length - len(line)) // 2) + right = " " * ((self.length - len(line)) // 2) + if len(line) % 2 != 0: + right += " " + elif alignment == self.align.RIGHT: + left = " " * (self.length - len(line)) + right = "" + else: + raise ValueError(f"Invalid alignment: {alignment}") + line = f"{left}{line}{right}" + self._print(self._lr_pad(line)) def print_border(self): """Print a full line using the border character.""" - print(self.border * (self.length + 6)) + self._print(self.border * (self.length + 6)) - def print_title(self, title): + def title(self, title, spacing_after: int = 2): """Print the main title element.""" - spacer = " " * (self.length - len(title)) - print(self.lr_pad(f"{title}{spacer}")) + self._print_line(title, self.align.CENTER) + for _ in range(spacing_after): + self.spacer() - def print_spacer(self): + def spacer(self): """Print an empty line with the border character only.""" - print(self.lr_pad(" " * self.length)) + self._print(self._lr_pad(" " * self.length)) - def print_subtitle(self, title): + def hr(self, char: str = "-"): + """Print a line with a horizontal rule.""" + self._print(self._lr_pad(char * self.length)) + + def subtitle(self, title: str, spacing_after: int = 1): """Print a subtitle for a section.""" title += ":" - spacer = " " * (self.length - len(title)) - print(self.lr_pad(f"{title}{spacer}")) + self._print_line(title, self.align.LEFT) + for _ in range(spacing_after): + self.spacer() - def print_list(self, items): + def list(self, items, spacing_after: int = 1): """Print a list of items, prepending a dash to each item.""" for item in items: - left_part = f" - {item}" - spacer = " " * (self.length - len(left_part)) - print(self.lr_pad(f"{left_part}{spacer}")) + self._print_line(f" - {item}", self.align.LEFT) + + for _ in range(spacing_after): + self.spacer() - def print_version(self, version): + def version(self, version): """Print the current ``version``.""" version = f"ver: {version}" - spacer = " " * (self.length - len(version)) - print(self.lr_pad(f"{spacer}{version}")) + self._print_line(version, self.align.RIGHT) - def lr_pad(self, content: str): - """Pad string content with defined border character. + def print(self, text: str): + """Print a line of text.""" + self._print_line(text, self.align.LEFT) - Args: - content: String content to pad + def left(self, text: str): + """Print a line of text left aligned. + + Same as `print` method. """ - return f"{self.border}{self.border} {content} {self.border}{self.border}" + self._print_line(text, self.align.LEFT) + + def centered(self, text: str): + """Print a line of text centered.""" + self._print_line(text, self.align.CENTER) + + def right(self, text: str): + """Print a line of text right aligned.""" + self._print_line(text, self.align.RIGHT) diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index 911a37d73e..c156ec9e4b 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -1,25 +1,25 @@ """Utilities related to logging.""" import asyncio +from datetime import datetime, timedelta +from io import TextIOWrapper import logging +from logging.config import fileConfig +from logging.handlers import BaseRotatingHandler import os -import pkg_resources -import sys from random import randint import re +import sys import time as mod_time +from typing import Optional, TextIO -from datetime import datetime, timedelta -from io import TextIOWrapper -from logging.handlers import BaseRotatingHandler -from logging.config import fileConfig -from portalocker import lock, unlock, LOCK_EX +import pkg_resources +from portalocker import LOCK_EX, lock, unlock from pythonjsonlogger import jsonlogger -from typing import Optional, TextIO +from ..config.settings import Settings from ..core.profile import Profile from ..version import __version__ from ..wallet.base import BaseWallet, DIDInfo - from .banner import Banner from .base import BaseSettings @@ -115,90 +115,82 @@ def print_banner( border """ print() - banner = Banner(border=border_character, length=banner_length) - banner.print_border() - - # Title - banner.print_title(agent_label or "ACA") - - banner.print_spacer() - banner.print_spacer() - - # Inbound transports - banner.print_subtitle("Inbound Transports") - internal_in_transports = [ - f"{transport.scheme}://{transport.host}:{transport.port}" - for transport in inbound_transports.values() - if not transport.is_external - ] - if internal_in_transports: - banner.print_spacer() - banner.print_list(internal_in_transports) - banner.print_spacer() - external_in_transports = [ - f"{transport.scheme}://{transport.host}:{transport.port}" - for transport in inbound_transports.values() - if transport.is_external - ] - if external_in_transports: - banner.print_spacer() - banner.print_subtitle(" External Plugin") - banner.print_spacer() - banner.print_list(external_in_transports) - banner.print_spacer() - - # Outbound transports - banner.print_subtitle("Outbound Transports") - internal_schemes = set().union( - *( - transport.schemes - for transport in outbound_transports.values() + with Banner(border=border_character, length=banner_length) as banner: + # Title + banner.title(agent_label or "ACA") + # Inbound transports + banner.subtitle("Inbound Transports") + internal_in_transports = [ + f"{transport.scheme}://{transport.host}:{transport.port}" + for transport in inbound_transports.values() if not transport.is_external - ) - ) - if internal_schemes: - banner.print_spacer() - banner.print_list([f"{scheme}" for scheme in sorted(internal_schemes)]) - banner.print_spacer() - - external_schemes = set().union( - *( - transport.schemes - for transport in outbound_transports.values() + ] + if internal_in_transports: + banner.list(internal_in_transports) + external_in_transports = [ + f"{transport.scheme}://{transport.host}:{transport.port}" + for transport in inbound_transports.values() if transport.is_external + ] + if external_in_transports: + banner.subtitle(" External Plugin") + banner.list(external_in_transports) + + # Outbound transports + banner.subtitle("Outbound Transports") + internal_schemes = set().union( + *( + transport.schemes + for transport in outbound_transports.values() + if not transport.is_external + ) + ) + if internal_schemes: + banner.list([f"{scheme}" for scheme in sorted(internal_schemes)]) + + external_schemes = set().union( + *( + transport.schemes + for transport in outbound_transports.values() + if transport.is_external + ) + ) + if external_schemes: + banner.subtitle(" External Plugin") + banner.list([f"{scheme}" for scheme in sorted(external_schemes)]) + + # DID info + if public_did: + banner.subtitle("Public DID Information") + banner.list([f"DID: {public_did}"]) + + # Admin server info + banner.subtitle("Administration API") + banner.list( + [f"http://{admin_server.host}:{admin_server.port}"] + if admin_server + else ["not enabled"] ) - ) - if external_schemes: - banner.print_spacer() - banner.print_subtitle(" External Plugin") - banner.print_spacer() - banner.print_list([f"{scheme}" for scheme in sorted(external_schemes)]) - banner.print_spacer() - - # DID info - if public_did: - banner.print_subtitle("Public DID Information") - banner.print_spacer() - banner.print_list([f"DID: {public_did}"]) - banner.print_spacer() - - # Admin server info - banner.print_subtitle("Administration API") - banner.print_spacer() - banner.print_list( - [f"http://{admin_server.host}:{admin_server.port}"] - if admin_server - else ["not enabled"] - ) - banner.print_spacer() - banner.print_version(__version__) + banner.version(__version__) - banner.print_border() print() print("Listening...") print() + @classmethod + def print_notices(cls, settings: Settings): + """Print notices and warnings.""" + if settings.get("wallet.type", "in_memory").lower() == "indy": + with Banner(border=":", length=80, file=sys.stderr) as banner: + banner.centered("⚠ DEPRECATION NOTICE: ⚠") + banner.hr() + banner.print( + "The Indy wallet type is deprecated, use Askar instead; see: " + "https://aca-py.org/main/deploying/IndySDKtoAskarMigration/", + ) + print() + ###################################################################### # Derived from diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 8952ccb986..0a53a420d4 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -318,6 +318,7 @@ async def start(self) -> None: self.setup_public_did and self.setup_public_did.did, self.admin_server, ) + LoggingConfigurator.print_notices(context.settings) # record ACA-Py version in Wallet, if needed from_version_storage = None diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index 525e52917b..a8bba88f7a 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -2,26 +2,24 @@ import asyncio import logging - from typing import Any, Mapping +import warnings from weakref import finalize, ref from ...config.injection_context import InjectionContext from ...config.provider import ClassProvider -from ...core.profile import Profile, ProfileManager, ProfileSession from ...core.error import ProfileError +from ...core.profile import Profile, ProfileManager, ProfileSession from ...ledger.base import BaseLedger from ...ledger.indy import IndySdkLedger, IndySdkLedgerPool from ...storage.base import BaseStorage, BaseStorageSearch from ...storage.vc_holder.base import VCHolder from ...wallet.base import BaseWallet from ...wallet.indy import IndySdkWallet - from ..holder import IndyHolder from ..issuer import IndyIssuer from ..verifier import IndyVerifier - -from .wallet_setup import IndyWalletConfig, IndyOpenWallet +from .wallet_setup import IndyOpenWallet, IndyWalletConfig LOGGER = logging.getLogger(__name__) @@ -191,6 +189,16 @@ async def open( self, context: InjectionContext, config: Mapping[str, Any] = None ) -> Profile: """Open an instance of an existing profile.""" + warnings.warn( + "Indy wallet type is deprecated, use Askar instead; see: " + "https://aca-py.org/main/deploying/IndySDKtoAskarMigration/", + DeprecationWarning, + ) + LOGGER.warning( + "Indy wallet type is deprecated, use Askar instead; see: " + "https://aca-py.org/main/deploying/IndySDKtoAskarMigration/", + ) + indy_config = IndyWalletConfig(config) opened = await indy_config.open_wallet() return IndySdkProfile(opened, context) diff --git a/requirements.dev.txt b/requirements.dev.txt index 0636eba1fa..87435ad141 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1,9 +1,9 @@ asynctest==0.13.0 async-case~=10.1 -pytest~=6.2 +pytest~=7.4.0 pytest-asyncio==0.14.0 pytest-cov==2.10.1 -pytest-flake8==1.0.6 +pytest-flake8==1.1.1 mock~=4.0 flake8==4.0.1