Edit on GitHub

communex._common

  1import random
  2import re
  3import warnings
  4from collections import defaultdict
  5from enum import Enum
  6from typing import Any, Callable, Mapping, TypeVar
  7
  8from pydantic import SecretStr
  9from pydantic_settings import BaseSettings, SettingsConfigDict
 10
 11from communex.balance import from_nano
 12from communex.types import Ss58Address
 13
 14IPFS_REGEX = re.compile(r"^Qm[1-9A-HJ-NP-Za-km-z]{44}$")
 15
 16
 17def deprecated(func: Callable[..., Any]) -> Callable[..., Any]:
 18    def wrapper(*args: Any, **kwargs: Any) -> Any:
 19        warnings.warn(
 20            f"The function {func.__name__} is deprecated and may be removed in a future version.",
 21            DeprecationWarning,
 22        )
 23        return func(*args, **kwargs)
 24
 25    return wrapper
 26
 27
 28class ComxSettings(BaseSettings):
 29    model_config = SettingsConfigDict(env_prefix="COMX_")
 30    # TODO: improve node lists
 31    NODE_URLS: list[str] = [
 32        "wss://api.communeai.net",
 33    ]
 34    TESTNET_NODE_URLS: list[str] = ["wss://testnet.api.communeai.net"]
 35    UNIVERSAL_PASSWORD: SecretStr | None = None
 36    KEY_PASSWORDS: dict[str, SecretStr] | None = None
 37
 38
 39def get_node_url(
 40    comx_settings: ComxSettings | None = None, *, use_testnet: bool = False
 41) -> str:
 42    comx_settings = comx_settings or ComxSettings()
 43    match use_testnet:
 44        case True:
 45            node_url = random.choice(comx_settings.TESTNET_NODE_URLS)
 46        case False:
 47            node_url = random.choice(comx_settings.NODE_URLS)
 48    return node_url
 49
 50
 51def get_available_nodes(
 52    comx_settings: ComxSettings | None = None, *, use_testnet: bool = False
 53) -> list[str]:
 54    comx_settings = comx_settings or ComxSettings()
 55
 56    match use_testnet:
 57        case True:
 58            node_urls = comx_settings.TESTNET_NODE_URLS
 59        case False:
 60            node_urls = comx_settings.NODE_URLS
 61    return node_urls
 62
 63
 64class BalanceUnit(str, Enum):
 65    joule = "joule"
 66    j = "j"
 67    nano = "nano"
 68    n = "n"
 69
 70
 71def format_balance(balance: int, unit: BalanceUnit = BalanceUnit.nano) -> str:
 72    """
 73    Formats a balance.
 74    """
 75
 76    match unit:
 77        case BalanceUnit.nano | BalanceUnit.n:
 78            return f"{balance}"
 79        case BalanceUnit.joule | BalanceUnit.j:
 80            in_joules = from_nano(balance)
 81            round_joules = round(in_joules, 4)
 82            return f"{round_joules:,} COMAI"
 83
 84
 85K = TypeVar("K")
 86V = TypeVar("V")
 87Z = TypeVar("Z")
 88
 89
 90def intersection_update(
 91    base: dict[K, V], update: dict[K, Z]
 92) -> Mapping[K, V | Z]:
 93    """
 94    Update a dictionary with another dictionary, but only with keys that are already present.
 95    """
 96    updated = {k: update[k] for k in base if k in update}
 97    return updated
 98
 99
100def transform_stake_dmap(
101    stake_storage: dict[tuple[Ss58Address, Ss58Address], int],
102) -> dict[Ss58Address, list[tuple[Ss58Address, int]]]:
103    """
104    Transforms either the StakeTo or StakeFrom storage into the stake legacy data type.
105    """
106    transformed: dict[Ss58Address, list[tuple[Ss58Address, int]]] = defaultdict(
107        list
108    )
109    [transformed[k1].append((k2, v)) for (k1, k2), v in stake_storage.items()]
110
111    return dict(transformed)
IPFS_REGEX = re.compile('^Qm[1-9A-HJ-NP-Za-km-z]{44}$')
def deprecated(func: Callable[..., Any]) -> Callable[..., Any]:
18def deprecated(func: Callable[..., Any]) -> Callable[..., Any]:
19    def wrapper(*args: Any, **kwargs: Any) -> Any:
20        warnings.warn(
21            f"The function {func.__name__} is deprecated and may be removed in a future version.",
22            DeprecationWarning,
23        )
24        return func(*args, **kwargs)
25
26    return wrapper
class ComxSettings(pydantic_settings.main.BaseSettings):
29class ComxSettings(BaseSettings):
30    model_config = SettingsConfigDict(env_prefix="COMX_")
31    # TODO: improve node lists
32    NODE_URLS: list[str] = [
33        "wss://api.communeai.net",
34    ]
35    TESTNET_NODE_URLS: list[str] = ["wss://testnet.api.communeai.net"]
36    UNIVERSAL_PASSWORD: SecretStr | None = None
37    KEY_PASSWORDS: dict[str, SecretStr] | None = None

Base class for settings, allowing values to be overridden by environment variables.

This is useful in production for secrets you do not wish to save in code, it plays nicely with docker(-compose), Heroku and any 12 factor app design.

All the below attributes can be set via model_config.

Arguments:
  • _case_sensitive: Whether environment variables names should be read with case-sensitivity. Defaults to None.
  • _env_prefix: Prefix for all environment variables. Defaults to None.
  • _env_file: The env file(s) to load settings values from. Defaults to Path(''), which means that the value from model_config['env_file'] should be used. You can also pass None to indicate that environment variables should not be loaded from an env file.
  • _env_file_encoding: The env file encoding, e.g. 'latin-1'. Defaults to None.
  • _env_ignore_empty: Ignore environment variables where the value is an empty string. Default to False.
  • _env_nested_delimiter: The nested env values delimiter. Defaults to None.
  • _env_parse_none_str: The env string value that should be parsed (e.g. "null", "void", "None", etc.) into None type(None). Defaults to None type(None), which means no parsing should occur.
  • _env_parse_enums: Parse enum field names to values. Defaults to None., which means no parsing should occur.
  • _cli_prog_name: The CLI program name to display in help text. Defaults to None if _cli_parse_args is None. Otherwse, defaults to sys.argv[0].
  • _cli_parse_args: The list of CLI arguments to parse. Defaults to None. If set to True, defaults to sys.argv[1:].
  • _cli_settings_source: Override the default CLI settings source with a user defined instance. Defaults to None.
  • _cli_parse_none_str: The CLI string value that should be parsed (e.g. "null", "void", "None", etc.) into None type(None). Defaults to _env_parse_none_str value if set. Otherwise, defaults to "null" if _cli_avoid_json is False, and "None" if _cli_avoid_json is True.
  • _cli_hide_none_type: Hide None values in CLI help text. Defaults to False.
  • _cli_avoid_json: Avoid complex JSON objects in CLI help text. Defaults to False.
  • _cli_enforce_required: Enforce required fields at the CLI. Defaults to False.
  • _cli_use_class_docs_for_groups: Use class docstrings in CLI group help text instead of field descriptions. Defaults to False.
  • _cli_prefix: The root parser command line arguments prefix. Defaults to "".
  • _secrets_dir: The secret files directory. Defaults to None.
model_config = {'extra': 'forbid', 'arbitrary_types_allowed': True, 'validate_default': True, 'case_sensitive': False, 'env_prefix': 'COMX_', 'env_file': None, 'env_file_encoding': None, 'env_ignore_empty': False, 'env_nested_delimiter': None, 'env_parse_none_str': None, 'env_parse_enums': None, 'cli_prog_name': None, 'cli_parse_args': None, 'cli_settings_source': None, 'cli_parse_none_str': None, 'cli_hide_none_type': False, 'cli_avoid_json': False, 'cli_enforce_required': False, 'cli_use_class_docs_for_groups': False, 'cli_prefix': '', 'json_file': None, 'json_file_encoding': None, 'yaml_file': None, 'yaml_file_encoding': None, 'toml_file': None, 'secrets_dir': None, 'protected_namespaces': ('model_', 'settings_')}
NODE_URLS: list[str]
TESTNET_NODE_URLS: list[str]
UNIVERSAL_PASSWORD: pydantic.types.SecretStr | None
KEY_PASSWORDS: dict[str, pydantic.types.SecretStr] | None
model_fields = {'NODE_URLS': FieldInfo(annotation=list[str], required=False, default=['wss://api.communeai.net']), 'TESTNET_NODE_URLS': FieldInfo(annotation=list[str], required=False, default=['wss://testnet.api.communeai.net']), 'UNIVERSAL_PASSWORD': FieldInfo(annotation=Union[SecretStr, NoneType], required=False, default=None), 'KEY_PASSWORDS': FieldInfo(annotation=Union[dict[str, SecretStr], NoneType], required=False, default=None)}
model_computed_fields = {}
Inherited Members
pydantic_settings.main.BaseSettings
BaseSettings
settings_customise_sources
pydantic.main.BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
def get_node_url( comx_settings: ComxSettings | None = None, *, use_testnet: bool = False) -> str:
40def get_node_url(
41    comx_settings: ComxSettings | None = None, *, use_testnet: bool = False
42) -> str:
43    comx_settings = comx_settings or ComxSettings()
44    match use_testnet:
45        case True:
46            node_url = random.choice(comx_settings.TESTNET_NODE_URLS)
47        case False:
48            node_url = random.choice(comx_settings.NODE_URLS)
49    return node_url
def get_available_nodes( comx_settings: ComxSettings | None = None, *, use_testnet: bool = False) -> list[str]:
52def get_available_nodes(
53    comx_settings: ComxSettings | None = None, *, use_testnet: bool = False
54) -> list[str]:
55    comx_settings = comx_settings or ComxSettings()
56
57    match use_testnet:
58        case True:
59            node_urls = comx_settings.TESTNET_NODE_URLS
60        case False:
61            node_urls = comx_settings.NODE_URLS
62    return node_urls
class BalanceUnit(builtins.str, enum.Enum):
65class BalanceUnit(str, Enum):
66    joule = "joule"
67    j = "j"
68    nano = "nano"
69    n = "n"

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

joule = <BalanceUnit.joule: 'joule'>
j = <BalanceUnit.j: 'j'>
nano = <BalanceUnit.nano: 'nano'>
n = <BalanceUnit.n: 'n'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
def format_balance( balance: int, unit: BalanceUnit = <BalanceUnit.nano: 'nano'>) -> str:
72def format_balance(balance: int, unit: BalanceUnit = BalanceUnit.nano) -> str:
73    """
74    Formats a balance.
75    """
76
77    match unit:
78        case BalanceUnit.nano | BalanceUnit.n:
79            return f"{balance}"
80        case BalanceUnit.joule | BalanceUnit.j:
81            in_joules = from_nano(balance)
82            round_joules = round(in_joules, 4)
83            return f"{round_joules:,} COMAI"

Formats a balance.

def intersection_update(base: dict[~K, ~V], update: dict[~K, ~Z]) -> Mapping[~K, Union[~V, ~Z]]:
91def intersection_update(
92    base: dict[K, V], update: dict[K, Z]
93) -> Mapping[K, V | Z]:
94    """
95    Update a dictionary with another dictionary, but only with keys that are already present.
96    """
97    updated = {k: update[k] for k in base if k in update}
98    return updated

Update a dictionary with another dictionary, but only with keys that are already present.

def transform_stake_dmap( stake_storage: dict[tuple[communex.types.Ss58Address, communex.types.Ss58Address], int]) -> dict[communex.types.Ss58Address, list[tuple[communex.types.Ss58Address, int]]]:
101def transform_stake_dmap(
102    stake_storage: dict[tuple[Ss58Address, Ss58Address], int],
103) -> dict[Ss58Address, list[tuple[Ss58Address, int]]]:
104    """
105    Transforms either the StakeTo or StakeFrom storage into the stake legacy data type.
106    """
107    transformed: dict[Ss58Address, list[tuple[Ss58Address, int]]] = defaultdict(
108        list
109    )
110    [transformed[k1].append((k2, v)) for (k1, k2), v in stake_storage.items()]
111
112    return dict(transformed)

Transforms either the StakeTo or StakeFrom storage into the stake legacy data type.