diff --git a/docs/source/api_doc/utils/index.rst b/docs/source/api_doc/utils/index.rst index efddb0cae586fd029efec50f473011bd9186c5ed..78bfa7c30587959f43fc0cb6861f3cf7b7e941cb 100644 --- a/docs/source/api_doc/utils/index.rst +++ b/docs/source/api_doc/utils/index.rst @@ -8,4 +8,5 @@ treevalue.utils enum final func + random singleton diff --git a/docs/source/api_doc/utils/random.rst b/docs/source/api_doc/utils/random.rst new file mode 100644 index 0000000000000000000000000000000000000000..c898eee31847c27da20fb8bc317177bed16d280a --- /dev/null +++ b/docs/source/api_doc/utils/random.rst @@ -0,0 +1,20 @@ +treevalue.utils.random +============================ + +seed_random +~~~~~~~~~~~~~~~~~~ + +.. autofunction:: treevalue.utils.random.seed_random + + +random_hex +~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: treevalue.utils.random.random_hex + + +random_hex_with_timestamp +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: treevalue.utils.random.random_hex_with_timestamp + diff --git a/test/utils/test_random.py b/test/utils/test_random.py index f64e8eba418949731516ed49fa7e814534a8396c..87dfca471a5bfd5746225e89b963892f6cd63fb2 100644 --- a/test/utils/test_random.py +++ b/test/utils/test_random.py @@ -1,6 +1,8 @@ +import re + import pytest -from treevalue.utils import seed_random +from treevalue.utils import seed_random, random_hex, random_hex_with_timestamp @pytest.mark.unittest @@ -13,3 +15,11 @@ class TestUtilsRandom: with seed_random(233) as rnd: a, b, c = rnd.randint(0x00, 0xff), rnd.randint(0x00, 0xff), rnd.randint(0x00, 0xff) assert (a, b, c) == (89, 118, 247) + + def test_random_hex(self): + assert re.fullmatch(r'^[a-f0-9]{32}$', random_hex()) + assert re.fullmatch(r'^[a-f0-9]{48}$', random_hex(48)) + + def test_random_hex_with_timestamp(self): + assert re.fullmatch(r'^\d{8}_\d{12}_[a-f0-9]{12}$', random_hex_with_timestamp()) + assert re.fullmatch(r'^\d{8}_\d{12}_[a-f0-9]{48}$', random_hex_with_timestamp(48)) diff --git a/treevalue/utils/__init__.py b/treevalue/utils/__init__.py index 7d575a86edf4fc145147c9ccd837b23e3c7e3442..5e3c23aead982bc2dbbb37d6e6615c5e5ca42da8 100644 --- a/treevalue/utils/__init__.py +++ b/treevalue/utils/__init__.py @@ -2,6 +2,6 @@ from .clazz import init_magic, class_wraps, common_bases, common_direct_base from .enum import int_enum_loads from .final import FinalMeta from .func import args_iter, dynamic_call -from .random import seed_random +from .random import seed_random, random_hex, random_hex_with_timestamp from .singleton import SingletonMeta, ValueBasedSingletonMeta, SingletonMark from .tree import build_tree diff --git a/treevalue/utils/random.py b/treevalue/utils/random.py index 71d76eaec8312fc45286ef974633d35e3265a2a5..7f24486d7ee5f78eea52d843fa0217b9f2343b71 100644 --- a/treevalue/utils/random.py +++ b/treevalue/utils/random.py @@ -1,4 +1,6 @@ +import random from contextlib import contextmanager +from datetime import datetime from random import Random @@ -11,9 +13,43 @@ def seed_random(seed): Arguments: - seed (:obj:`int`): Random seed, should be a `int`. """ - random = Random() - random.seed(seed) + rnd = Random() + rnd.seed(seed) try: - yield random + yield rnd finally: - random.seed() + rnd.seed() + + +def random_hex(length: int = 32) -> str: + """ + Overview: + Generate random hex string. + + Arguments: + - length (:obj:`int`): Length of hex string, default is `32`. + + Returns: + - string (:obj:`str`): Generated string. + + Examples: + >>> random_hex() # 'ca7f14b25aa4498efdacb54e9ff72784' + """ + return ''.join([hex(random.randint(0, 15))[2:] for _ in range(length)]) + + +def random_hex_with_timestamp(length: int = 12) -> str: + """ + Overview: + Generate random hex string, with prefix of timestamp. + + Arguments: + - length (:obj:`int`): Length of hex string, default is `12`. + + Returns: + - string (:obj:`str`): Generated string. + + Examples: + >>> random_hex_with_timestamp() # '20210729_202059576266_69603d64afad' + """ + return datetime.now().strftime("%Y%m%d_%H%M%S%f") + "_" + random_hex(length) diff --git a/treevalue/utils/tree.py b/treevalue/utils/tree.py index 967b9d78ab5197e1e057886b54b68e6088212742..0178b74b5a432a24e1f47ea69fbf1318c0991eb9 100644 --- a/treevalue/utils/tree.py +++ b/treevalue/utils/tree.py @@ -1,7 +1,11 @@ +import re from queue import Queue +from graphviz import Digraph from treelib import Tree as LibTree +from .random import random_hex_with_timestamp + _ROOT_ID = '_root' _NODE_ID_TEMP = '_node_{id}' @@ -31,3 +35,23 @@ def build_tree(root, represent=None, iterate=None, recurse=None) -> LibTree: _queue.put((_current_id, value)) return _tree + + +_NAME_PATTERN = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') + + +def _title_flatten(title): + title = re.sub(r'[^a-zA-Z0-9_]+', '_', str(title)) + title = re.sub(r'_+', '_', title) + title = title.strip('_').lower() + return title + + +def build_graph(root, name=None, title=None, represent=None, iterate=None, recurse=None): + represent = represent or repr + iterate = iterate or (lambda x: x.items()) + recurse = recurse or (lambda x: hasattr(x, 'items')) + + title = title or 'untitled_' + random_hex_with_timestamp() + name = name or _title_flatten(title) + graph = Digraph(name=name, comment=title)