annet 2.1.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of annet might be problematic. Click here for more details.
- {annet-2.1.0/annet.egg-info → annet-2.2.0}/PKG-INFO +1 -1
- {annet-2.1.0 → annet-2.2.0}/annet/deploy.py +9 -1
- {annet-2.1.0 → annet-2.2.0}/annet/deploy_ui.py +160 -125
- {annet-2.1.0 → annet-2.2.0}/annet/text_term_format.py +1 -1
- {annet-2.1.0 → annet-2.2.0/annet.egg-info}/PKG-INFO +1 -1
- {annet-2.1.0 → annet-2.2.0}/AUTHORS +0 -0
- {annet-2.1.0 → annet-2.2.0}/LICENSE +0 -0
- {annet-2.1.0 → annet-2.2.0}/MANIFEST.in +0 -0
- {annet-2.1.0 → annet-2.2.0}/README.md +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/fetchers/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/fetchers/stub/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/fetchers/stub/fetcher.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/file/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/file/provider.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/common/client.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/common/manufacturer.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/common/models.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/common/query.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/common/storage_opts.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/provider.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/adapters/netbox/v37/storage.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annet.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/command.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/diff.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/errors.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/filter_acl.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/jsontools.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/lib.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/netdev/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/netdev/db.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/netdev/devdb/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/netdev/devdb/data/devdb.json +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/netdev/views/hardware.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/output.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/patching.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/rbparser/acl.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/rbparser/platform.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/rulebook/common.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/tabparser.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/annlib/types.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/api/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/argparse.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/bgp_models.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/cli.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/cli_args.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/configs/context.yml +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/configs/logging.yaml +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/connectors.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/diff.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/executor.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/filtering.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/gen.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/base.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/common/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/common/initial.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/entire.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/exceptions.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/jsonfragment.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/partial.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/perf.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/ref.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/generators/result.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/hardware.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/implicit.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/lib.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/basemodel.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/device_models.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/executor.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/match_args.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/models_converter.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/peer_models.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/port_processor.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/mesh/registry.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/output.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/parallel.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/patching.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/reference.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl/action.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl/condition.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl/match_builder.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl/policy.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl/result.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl/routemap.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl/statement_builder.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/aspath.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/community.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/cumulus_frr.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/entities.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/execute.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/policy.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/prefix_lists.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rpl_generators/rd.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/arista/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/arista/aaa.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/arista/iface.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/aruba/ap_env.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/aruba/misc.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/b4com/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/b4com/file.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/b4com/iface.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/cisco/iface.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/cisco/misc.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/common.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/deploying.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/huawei/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/huawei/iface.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/huawei/misc.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/nexus/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/nexus/iface.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/patching.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/routeros/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/routeros/file.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/arista.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/arista.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/aruba.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/b4com.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/b4com.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/b4com.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/cisco.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/cisco.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/huawei.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/huawei.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/huawei.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/juniper.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/nexus.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/pc.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/pc.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/pc.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/routeros.order +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/storage.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/tabparser.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/tracing.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet/types.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet.egg-info/SOURCES.txt +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet.egg-info/dependency_links.txt +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet.egg-info/entry_points.txt +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet.egg-info/requires.txt +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet.egg-info/top_level.txt +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/example/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/example/lldp.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/mesh_example/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/mesh_example/bgp.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/mesh_example/mesh_logic.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/rpl_example/__init__.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/rpl_example/generator.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/rpl_example/items.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/rpl_example/mesh.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/annet_generators/rpl_example/route_policy.py +0 -0
- {annet-2.1.0 → annet-2.2.0}/requirements.txt +0 -0
- {annet-2.1.0 → annet-2.2.0}/setup.cfg +0 -0
- {annet-2.1.0 → annet-2.2.0}/setup.py +0 -0
|
@@ -23,6 +23,14 @@ class ProgressBar(abc.ABC):
|
|
|
23
23
|
def set_content(self, tile_name: str, content: str):
|
|
24
24
|
...
|
|
25
25
|
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def add_content(self, tile_name: str, content: str):
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def reset_content(self, tile_name: str):
|
|
32
|
+
...
|
|
33
|
+
|
|
26
34
|
@abc.abstractmethod
|
|
27
35
|
def set_progress(self,
|
|
28
36
|
tile_name: str,
|
|
@@ -36,7 +44,7 @@ class ProgressBar(abc.ABC):
|
|
|
36
44
|
...
|
|
37
45
|
|
|
38
46
|
@abc.abstractmethod
|
|
39
|
-
def set_exception(self, tile_name: str, cmd_exc: str, last_cmd: str, progress_max: int):
|
|
47
|
+
def set_exception(self, tile_name: str, cmd_exc: str, last_cmd: str, progress_max: int, content: str = "") -> None:
|
|
40
48
|
...
|
|
41
49
|
|
|
42
50
|
|
|
@@ -7,12 +7,16 @@ import math
|
|
|
7
7
|
import asyncio
|
|
8
8
|
import textwrap
|
|
9
9
|
import time
|
|
10
|
+
import traceback
|
|
10
11
|
from contextlib import contextmanager
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import Enum
|
|
11
14
|
from typing import Dict, List, Optional, Any
|
|
12
15
|
|
|
13
16
|
from contextlog import get_logger
|
|
14
17
|
|
|
15
18
|
from annet import text_term_format
|
|
19
|
+
from annet.deploy import ProgressBar
|
|
16
20
|
from annet.output import TextArgs
|
|
17
21
|
try:
|
|
18
22
|
import curses
|
|
@@ -21,6 +25,7 @@ except ImportError:
|
|
|
21
25
|
|
|
22
26
|
uname = os.uname()[0]
|
|
23
27
|
NCURSES_SIZE_T = 2 ** 15 - 1
|
|
28
|
+
MIN_CONTENT_HEIGHT = 20
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
class AskConfirm:
|
|
@@ -348,20 +353,42 @@ def init_colors():
|
|
|
348
353
|
}
|
|
349
354
|
|
|
350
355
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
356
|
+
@dataclass
|
|
357
|
+
class Tile:
|
|
358
|
+
win: "curses.window | None"
|
|
359
|
+
content: list[list[TextArgs]]
|
|
360
|
+
title: list[str]
|
|
361
|
+
height: int
|
|
362
|
+
width: int
|
|
363
|
+
need_draw: bool = True
|
|
364
|
+
total: int = 0
|
|
365
|
+
iteration: int = 0
|
|
355
366
|
|
|
367
|
+
|
|
368
|
+
class UiState(Enum):
|
|
369
|
+
INIT = "INIT"
|
|
370
|
+
OK = "OK"
|
|
371
|
+
WAIT_INPUT = "WAIT_INPUT"
|
|
372
|
+
CLOSED = "CLOSED"
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class TailMode(Enum):
|
|
376
|
+
UNIFORM = "UNIFORM" # display is split into several parts with content and headers
|
|
377
|
+
ONE_CONTENT = "ONE_CONTENT" # only one part displays content, others show headers
|
|
378
|
+
NO_CONTENT = "NO_CONTENT" # only headers
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class ProgressBars(ProgressBar):
|
|
356
382
|
def __init__(self, tiles_params: dict[str, dict[Any, Any]]):
|
|
357
383
|
self.tiles_params = tiles_params
|
|
358
|
-
self.mode =
|
|
359
|
-
self.screen: "curses.window" = None
|
|
360
|
-
self.tiles: dict[str,
|
|
384
|
+
self.mode: TailMode = TailMode.UNIFORM
|
|
385
|
+
self.screen: "curses.window | None" = None
|
|
386
|
+
self.tiles: dict[str, Tile] = {}
|
|
361
387
|
self.offset = [0, 0]
|
|
362
388
|
self.terminal_refresher_coro = None
|
|
363
389
|
self.color_to_curses: dict[Optional[str], int] = {}
|
|
364
|
-
self.state =
|
|
390
|
+
self.state: UiState = UiState.INIT
|
|
391
|
+
self.active_tile: int = len(tiles_params) - 1 # tiles with content have numbers from 1
|
|
365
392
|
|
|
366
393
|
# context
|
|
367
394
|
self.enter_ok = False
|
|
@@ -397,14 +424,14 @@ class ProgressBars:
|
|
|
397
424
|
|
|
398
425
|
def evaluate_mode(self, scree_size):
|
|
399
426
|
height = scree_size[0] // len(self.tiles_params)
|
|
400
|
-
if height >
|
|
401
|
-
return
|
|
402
|
-
if scree_size[0] - len(self.tiles_params) >
|
|
403
|
-
return
|
|
427
|
+
if height > MIN_CONTENT_HEIGHT:
|
|
428
|
+
return TailMode.UNIFORM
|
|
429
|
+
if scree_size[0] - len(self.tiles_params) > MIN_CONTENT_HEIGHT:
|
|
430
|
+
return TailMode.ONE_CONTENT
|
|
404
431
|
if scree_size[0] // len(self.tiles_params) > 1:
|
|
405
|
-
return
|
|
432
|
+
return TailMode.NO_CONTENT
|
|
406
433
|
else:
|
|
407
|
-
return
|
|
434
|
+
return TailMode.NO_CONTENT
|
|
408
435
|
|
|
409
436
|
def make_tiles(self):
|
|
410
437
|
import curses
|
|
@@ -421,8 +448,10 @@ class ProgressBars:
|
|
|
421
448
|
begin_x = 0
|
|
422
449
|
tile_no = 0
|
|
423
450
|
status_bar_win = curses.newwin(1, width, scree_size[0], 0)
|
|
424
|
-
self.tiles["status:"] =
|
|
425
|
-
|
|
451
|
+
self.tiles["status:"] = Tile(
|
|
452
|
+
win=status_bar_win, content=[], title=[""],
|
|
453
|
+
height=1, width=width, need_draw=True,
|
|
454
|
+
)
|
|
426
455
|
max_tile_name_len = max(len(tile_name) for tile_name in self.tiles_params)
|
|
427
456
|
|
|
428
457
|
for tile_name in self.tiles_params:
|
|
@@ -430,16 +459,16 @@ class ProgressBars:
|
|
|
430
459
|
height = 0
|
|
431
460
|
tile_no += 1
|
|
432
461
|
|
|
433
|
-
if mode
|
|
462
|
+
if mode is TailMode.UNIFORM:
|
|
434
463
|
height = int(scree_size[0] // len(self.tiles_params)) # TODO:остаток от деления прибавить к последнему
|
|
435
464
|
win = curses.newwin(height, width, begin_y, begin_x)
|
|
436
|
-
elif mode
|
|
437
|
-
if i ==
|
|
465
|
+
elif mode is TailMode.ONE_CONTENT:
|
|
466
|
+
if i == self.active_tile:
|
|
438
467
|
height = scree_size[0] - tiles_count + 1
|
|
439
468
|
else:
|
|
440
469
|
height = 1
|
|
441
470
|
win = curses.newwin(height, width, begin_y, begin_x)
|
|
442
|
-
elif mode
|
|
471
|
+
elif mode is TailMode.NO_CONTENT:
|
|
443
472
|
height = 1
|
|
444
473
|
|
|
445
474
|
if tile_no < max_height:
|
|
@@ -449,26 +478,51 @@ class ProgressBars:
|
|
|
449
478
|
win = curses.newwin(height, width, begin_y, begin_x)
|
|
450
479
|
if tile_no == max_height:
|
|
451
480
|
left = len(self.tiles_params) - max_height + 1
|
|
452
|
-
self.tiles["dumb"] =
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
"
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
"height": height,
|
|
466
|
-
"width": width,
|
|
467
|
-
"need_draw": True
|
|
468
|
-
}
|
|
481
|
+
self.tiles["dumb"] = Tile(
|
|
482
|
+
win=curses.newwin(height, width, begin_y, begin_x),
|
|
483
|
+
content=[],
|
|
484
|
+
title=["... and %s more" % left],
|
|
485
|
+
height=height, width=width, need_draw=True,
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
self.tiles[tile_name] = Tile(
|
|
489
|
+
win=win,
|
|
490
|
+
content=[],
|
|
491
|
+
title=[("{:<%s}" % (max_tile_name_len)).format(tile_name)],
|
|
492
|
+
height=height, width=width, need_draw=True,
|
|
493
|
+
)
|
|
469
494
|
i += 1
|
|
470
495
|
begin_y += height
|
|
471
496
|
|
|
497
|
+
def _next_active_tile(self):
|
|
498
|
+
self._set_active_tile((self.active_tile + 1) % len(self.tiles_params))
|
|
499
|
+
|
|
500
|
+
def _prev_active_tile(self):
|
|
501
|
+
self._set_active_tile((self.active_tile - 1) % len(self.tiles_params))
|
|
502
|
+
|
|
503
|
+
def _set_active_tile(self, active_tile: int) -> None:
|
|
504
|
+
self.active_tile = active_tile
|
|
505
|
+
if self.mode is TailMode.ONE_CONTENT:
|
|
506
|
+
return
|
|
507
|
+
|
|
508
|
+
scree_size = self.screen.getmaxyx()
|
|
509
|
+
scree_offset = self.screen.getbegyx()
|
|
510
|
+
tiles_count = len(self.tiles_params)
|
|
511
|
+
width = scree_size[1]
|
|
512
|
+
begin_y = scree_offset[0]
|
|
513
|
+
for n, dev in enumerate(self.tiles_params):
|
|
514
|
+
tile = self.tiles[dev]
|
|
515
|
+
if n == self.active_tile:
|
|
516
|
+
height = scree_size[0] - tiles_count
|
|
517
|
+
else:
|
|
518
|
+
height = 1
|
|
519
|
+
tile.height = height
|
|
520
|
+
tile.win.resize(height, width)
|
|
521
|
+
tile.win.mvwin(begin_y, scree_offset[1])
|
|
522
|
+
begin_y += tile.height
|
|
523
|
+
tile.need_draw = True
|
|
524
|
+
self.refresh_all()
|
|
525
|
+
|
|
472
526
|
def set_status(self):
|
|
473
527
|
total = 0
|
|
474
528
|
iteration = 0
|
|
@@ -476,10 +530,8 @@ class ProgressBars:
|
|
|
476
530
|
for tile_name, tile in self.tiles.items():
|
|
477
531
|
if tile_name == "status:":
|
|
478
532
|
continue
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
total += tile["total"]
|
|
482
|
-
iteration += tile["iteration"]
|
|
533
|
+
total += tile.total
|
|
534
|
+
iteration += tile.iteration
|
|
483
535
|
if total > 0 and iteration > 0:
|
|
484
536
|
done_percent = float(iteration) / total * 100
|
|
485
537
|
|
|
@@ -548,7 +600,7 @@ class ProgressBars:
|
|
|
548
600
|
curses.curs_set(0)
|
|
549
601
|
curses.start_color()
|
|
550
602
|
self.color_to_curses = init_colors()
|
|
551
|
-
self.state =
|
|
603
|
+
self.state = UiState.OK
|
|
552
604
|
self.make_tiles()
|
|
553
605
|
self.progress_length = scree_size[1] // 3
|
|
554
606
|
|
|
@@ -559,34 +611,33 @@ class ProgressBars:
|
|
|
559
611
|
curses.nocbreak()
|
|
560
612
|
curses.echo()
|
|
561
613
|
curses.endwin()
|
|
562
|
-
self.state =
|
|
614
|
+
self.state = UiState.CLOSED
|
|
563
615
|
|
|
564
616
|
def draw_content(self, tile_name):
|
|
565
617
|
tile = self.tiles[tile_name]
|
|
566
|
-
win = tile
|
|
618
|
+
win = tile.win
|
|
567
619
|
size = win.getmaxyx()
|
|
568
620
|
margin = 1
|
|
569
621
|
if (size[0] - 2 * margin) <= 0:
|
|
570
622
|
return
|
|
571
|
-
|
|
572
|
-
draw_lines_in_win(res, win, color_to_curses=self.color_to_curses, margin=margin)
|
|
623
|
+
draw_lines_in_win(tile.content, win, color_to_curses=self.color_to_curses, margin=margin)
|
|
573
624
|
|
|
574
625
|
def draw_title(self, tile_name):
|
|
575
626
|
tile = self.tiles[tile_name]
|
|
576
|
-
title = tile
|
|
577
|
-
win = tile
|
|
627
|
+
title = tile.title
|
|
628
|
+
win = tile.win
|
|
578
629
|
if not isinstance(title, (tuple, list)):
|
|
579
630
|
title = [title]
|
|
580
|
-
draw_lines_in_win(
|
|
631
|
+
draw_lines_in_win([title], win, color_to_curses=self.color_to_curses, x_margin=1)
|
|
581
632
|
|
|
582
633
|
def refresh(self, tile_name: str, noutrefresh: bool = False):
|
|
583
634
|
# see noutrefresh in curses doc
|
|
584
635
|
tile = self.tiles[tile_name]
|
|
585
|
-
win = tile
|
|
586
|
-
if not tile
|
|
636
|
+
win = tile.win
|
|
637
|
+
if not tile.need_draw or win is None:
|
|
587
638
|
return
|
|
588
639
|
win.clear()
|
|
589
|
-
if tile
|
|
640
|
+
if tile.height > 1:
|
|
590
641
|
win.border()
|
|
591
642
|
self.draw_title(tile_name)
|
|
592
643
|
self.draw_content(tile_name)
|
|
@@ -594,12 +645,17 @@ class ProgressBars:
|
|
|
594
645
|
win.noutrefresh()
|
|
595
646
|
else:
|
|
596
647
|
win.refresh()
|
|
597
|
-
tile
|
|
648
|
+
tile.need_draw = False
|
|
598
649
|
|
|
599
650
|
def refresh_all(self):
|
|
600
|
-
if self.state
|
|
651
|
+
if self.state is UiState.CLOSED:
|
|
601
652
|
return
|
|
602
|
-
self.
|
|
653
|
+
if self.state is UiState.OK:
|
|
654
|
+
ch_list = self.get_pressed_keys()
|
|
655
|
+
if "\t" in ch_list: # Tab
|
|
656
|
+
self._next_active_tile()
|
|
657
|
+
if "\x1b[Z" in ch_list: # shift-Tab
|
|
658
|
+
self._prev_active_tile()
|
|
603
659
|
self.screen.refresh()
|
|
604
660
|
self.set_status()
|
|
605
661
|
tile_name = None
|
|
@@ -611,21 +667,36 @@ class ProgressBars:
|
|
|
611
667
|
def set_title(self, tile_name, title):
|
|
612
668
|
tile = self.tiles[tile_name]
|
|
613
669
|
# в 0 элементе хранится выровненный хостнейм
|
|
614
|
-
title0 = tile
|
|
615
|
-
new_title =
|
|
616
|
-
if new_title == tile
|
|
670
|
+
title0 = tile.title[0]
|
|
671
|
+
new_title = [title0, title]
|
|
672
|
+
if new_title == tile.title:
|
|
617
673
|
return
|
|
618
|
-
tile
|
|
619
|
-
tile
|
|
674
|
+
tile.title = new_title
|
|
675
|
+
tile.need_draw = True
|
|
620
676
|
if not self.terminal_refresher_coro:
|
|
621
677
|
self.refresh(tile_name)
|
|
622
678
|
|
|
623
679
|
def set_content(self, tile_name: str, content: str):
|
|
624
680
|
tile = self.tiles[tile_name]
|
|
625
|
-
|
|
681
|
+
new_content = list(text_term_format.curses_format(content, "switch_out").values())
|
|
682
|
+
if new_content == tile.content:
|
|
626
683
|
return
|
|
627
|
-
tile
|
|
628
|
-
tile
|
|
684
|
+
tile.need_draw = True
|
|
685
|
+
tile.content = new_content
|
|
686
|
+
if not self.terminal_refresher_coro:
|
|
687
|
+
self.refresh(tile_name)
|
|
688
|
+
|
|
689
|
+
def add_content(self, tile_name: str, content: str):
|
|
690
|
+
tile = self.tiles[tile_name]
|
|
691
|
+
tile.need_draw = True
|
|
692
|
+
tile.content.extend(text_term_format.curses_format(content, "switch_out").values())
|
|
693
|
+
if not self.terminal_refresher_coro:
|
|
694
|
+
self.refresh(tile_name)
|
|
695
|
+
|
|
696
|
+
def reset_content(self, tile_name: str):
|
|
697
|
+
tile = self.tiles[tile_name]
|
|
698
|
+
tile.need_draw = True
|
|
699
|
+
tile.content = []
|
|
629
700
|
if not self.terminal_refresher_coro:
|
|
630
701
|
self.refresh(tile_name)
|
|
631
702
|
|
|
@@ -655,50 +726,66 @@ class ProgressBars:
|
|
|
655
726
|
bar = fill * filled_length + "-" * (self.progress_length - filled_length)
|
|
656
727
|
res = "%s |%s| %s%% %s" % (prefix, bar, percent, suffix)
|
|
657
728
|
tile = self.tiles[tile_name]
|
|
658
|
-
tile
|
|
659
|
-
tile
|
|
729
|
+
tile.total = total
|
|
730
|
+
tile.iteration = iteration
|
|
660
731
|
if error:
|
|
661
732
|
res = TextArgs(res, "red")
|
|
662
733
|
else:
|
|
663
734
|
res = TextArgs(res, "cyan")
|
|
664
735
|
self.set_title(tile_name, res)
|
|
665
736
|
|
|
666
|
-
def set_exception(self,
|
|
737
|
+
def set_exception(self, tile_name: str, cmd_exc: str, last_cmd: str, progress_max: int, content: str = ""):
|
|
667
738
|
suffix = "cmd error: %s %s" % (str(cmd_exc).strip().replace("\n", "--"), last_cmd)
|
|
668
|
-
self.set_progress(
|
|
739
|
+
self.set_progress(tile_name, progress_max, progress_max, suffix=suffix, error=True)
|
|
740
|
+
if content:
|
|
741
|
+
self.add_content(tile_name, content)
|
|
669
742
|
|
|
670
743
|
def get_pressed_keys(self):
|
|
671
|
-
ch_list =
|
|
744
|
+
ch_list = ""
|
|
672
745
|
while True:
|
|
673
746
|
try:
|
|
674
747
|
ch = self.screen.getkey()
|
|
675
|
-
ch_list
|
|
748
|
+
ch_list += ch
|
|
676
749
|
except Exception:
|
|
677
750
|
time.sleep(0.01)
|
|
678
751
|
break
|
|
679
752
|
return ch_list
|
|
680
753
|
|
|
681
754
|
async def wait_for_exit(self):
|
|
755
|
+
self.state = UiState.WAIT_INPUT
|
|
682
756
|
while True:
|
|
683
757
|
ch_list = self.get_pressed_keys()
|
|
684
758
|
if ch_list:
|
|
685
|
-
get_logger().debug("read ch %s", ch_list)
|
|
759
|
+
get_logger().debug("read ch %s", repr(ch_list))
|
|
760
|
+
if "\t" in ch_list: # Tab
|
|
761
|
+
self._next_active_tile()
|
|
762
|
+
if "\x1b[Z" in ch_list: # shift-Tab
|
|
763
|
+
self._prev_active_tile()
|
|
686
764
|
if "q" in ch_list:
|
|
687
765
|
return
|
|
688
766
|
else:
|
|
689
767
|
await asyncio.sleep(0.001)
|
|
690
768
|
|
|
691
769
|
|
|
692
|
-
def draw_lines_in_win(
|
|
770
|
+
def draw_lines_in_win(
|
|
771
|
+
lines: list[list[TextArgs]],
|
|
772
|
+
win: "curses.window | None",
|
|
773
|
+
color_to_curses: dict[Optional[str], int],
|
|
774
|
+
margin: int = 0,
|
|
775
|
+
x_margin: int = 0,
|
|
776
|
+
y_margin: int = 0,
|
|
777
|
+
) -> None:
|
|
778
|
+
if win is None:
|
|
779
|
+
return
|
|
693
780
|
max_y, max_x = win.getmaxyx()
|
|
694
781
|
max_y -= 2 * (margin or y_margin)
|
|
695
782
|
max_x -= 2 * (margin or x_margin)
|
|
696
|
-
lines_count =
|
|
783
|
+
lines_count = len(lines)
|
|
697
784
|
if lines_count > max_y:
|
|
698
785
|
start_line = lines_count - max_y + 1
|
|
699
786
|
else:
|
|
700
787
|
start_line = 0
|
|
701
|
-
for line_no, line_data in
|
|
788
|
+
for line_no, line_data in enumerate(lines[start_line:]):
|
|
702
789
|
line_no = line_no - start_line
|
|
703
790
|
line_pos_calc = 0
|
|
704
791
|
for line_part in line_data:
|
|
@@ -720,55 +807,3 @@ def draw_lines_in_win(lines, win, color_to_curses: dict[Optional[str], int], mar
|
|
|
720
807
|
except Exception as exp:
|
|
721
808
|
get_logger().error("y=%s, x=%s, text=%s %s", y, x, text, exp)
|
|
722
809
|
line_pos_calc += len(text)
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
def wrap_to_windows(window, text: str, margin: int = 2) -> List[str]:
|
|
726
|
-
y, x = window.getmaxyx()
|
|
727
|
-
wrapped_text = textwrap.wrap(text, x - margin, max_lines=y, subsequent_indent="+")
|
|
728
|
-
return wrapped_text
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
def curses_input(prompt: str, screen, header: Dict[int, str], hide_input=False):
|
|
732
|
-
import curses
|
|
733
|
-
|
|
734
|
-
if not header:
|
|
735
|
-
text = {0: prompt}
|
|
736
|
-
else:
|
|
737
|
-
text = header.copy()
|
|
738
|
-
text[max(header) + 1] = prompt
|
|
739
|
-
lines = [line for text_item in text.values() for line in wrap_to_windows(screen, text_item)]
|
|
740
|
-
text = dict(enumerate(lines))
|
|
741
|
-
maxy, maxx = screen.getmaxyx()
|
|
742
|
-
curses.init_pair(12, curses.COLOR_RED, curses.COLOR_WHITE)
|
|
743
|
-
input_len = 35
|
|
744
|
-
res: list[str] = []
|
|
745
|
-
text_width = max([len(x) for x in text.values()] + [len(prompt) + input_len])
|
|
746
|
-
header_height = max(list(text) + [0]) + 1
|
|
747
|
-
begin_y = int(maxy / 2)
|
|
748
|
-
begin_x = int(maxx / 2 - text_width / 2)
|
|
749
|
-
nlines = header_height + 2
|
|
750
|
-
ncols = text_width + 2
|
|
751
|
-
win = curses.newwin(nlines, ncols, begin_y, begin_x)
|
|
752
|
-
curses.noecho()
|
|
753
|
-
prev_curs = curses.curs_set(1)
|
|
754
|
-
while True:
|
|
755
|
-
win.clear()
|
|
756
|
-
win.refresh()
|
|
757
|
-
win.bkgd(curses.color_pair(12))
|
|
758
|
-
win.box()
|
|
759
|
-
draw_lines_in_win(text, win, margin=1, color_to_curses={})
|
|
760
|
-
if hide_input:
|
|
761
|
-
win.addstr(header_height, len(prompt) + 1, "*" * len(res))
|
|
762
|
-
else:
|
|
763
|
-
win.addstr(header_height, len(prompt) + 1, "".join(res))
|
|
764
|
-
ch = win.getkey()
|
|
765
|
-
if ch == "\x7f":
|
|
766
|
-
if res:
|
|
767
|
-
res.pop(-1)
|
|
768
|
-
elif ch == "\n":
|
|
769
|
-
break
|
|
770
|
-
else:
|
|
771
|
-
res.append(ch)
|
|
772
|
-
win.erase()
|
|
773
|
-
curses.curs_set(prev_curs)
|
|
774
|
-
return "".join(res)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|