Skip to content

Commit

Permalink
ver 0.1.2-alpha
Browse files Browse the repository at this point in the history
  • Loading branch information
blademd committed May 24, 2023
1 parent 7f3a7c4 commit 9f165a3
Show file tree
Hide file tree
Showing 18 changed files with 663 additions and 125 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ MANIFEST
*.conf
*.cfg

saves/
settings/

# Installer logs
pip-log.txt
pip-delete-this-directory.txt
Expand Down
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Currently, Thymus supports only the Juniper Junos OS tree-view configuration (*t

## Requirements

Tested with Python **3.8.10**.

Thymus uses [Textual](https://github.com/Textualize/textual) as its TUI part so all the requirements of the latter are applicable to the former. There are no additional requirements (except your courage for sure).

## Modes
Expand Down Expand Up @@ -61,6 +63,24 @@ From the Textual documentation:

# Usage

## Context vs. global

When you open a config file you get access to the Working view. This view allows you to navigate through the config file, filter some output, save it, etc. Thymus supports two configurable instances: context and global. The context instance describes the settings of a configuration that is opened in the Working view. Currently, there is only the JunOS context available. The global instance allows you to manipulate settings of Thymus itself. Context's settings are manipulated via the `set` command, global -- the `global set` command.

The global settings are stored in the `thymus\settings\global.json` file. Thymus creates this directory and file at its startup if it is able to.

## Global commands list

- `global show themes` command lists all available highlighting themes.
- `global set theme` command allows you to choose any theme from the list.

## Context commands list

- `set` command allows you to configure some system-wide settings for the current context. It supports now:
- - `name` is to set the name of the context. This name is required by the `compare` modificator of the `show` command.
- - `spaces` is to set the indentation length for the `show` command.
- - `encoding` is to set the encoding for the `save` modificator.

## Junos commands list

The behavior of the tool mimics Junos CLI. Some commands are replaced with more handy analogs.
Expand All @@ -70,11 +90,7 @@ The behavior of the tool mimics Junos CLI. Some commands are replaced with more
- `top` command without any arguments sets the current path back to the root.
- - `top show` modification allows you to see the full configuration (the configuration of the root). It also supports relative paths as the `show` does.
- - `top go` modification allows you to change the current path to any other possible section.
- `up` command without any arguments sets the current path back to the previous section. It supports a number of sections to set back. If the number is bigger than the current depth `up` works as `top`.
- `set` command allows you to configure some system-wide settings for the current screen. Currently, it supports:
- - `name` is to set the name of the screen/context. This name is required by the `compare` modificator of the `show` command.
- - `spaces` is to set the indentation length for the `show` command.
- - `encoding` is to set the encoding for the `save` modificator.
- `up` command without any arguments sets the current path back to the previous section. It supports a number of sections to return. If the number is bigger than the current depth `up` works as `top`. It also supports `show` instead the number, `up show` does not support any arguments or modificators at this moment.

And some commands are for the CLI-mode:

Expand Down Expand Up @@ -145,7 +161,7 @@ The sidebar shows you sections for autocompleting your current input. It works w

## What's next

- Syntax highlighting.
- Configs` analyzing.
- Other NOS`es support.

## Feedback
Expand Down
13 changes: 12 additions & 1 deletion release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,15 @@

* Textual support updated to the latest version (v0.24.1).
* The open dialog was redesigned.
* An extended textlog widged's code was simplified with the scroll_end option.
* An extended textlog widged's code was simplified with the scroll_end option.

# Version 0.1.2-alpha

* The main menu was redesigned.
* Thymus supports settings and the settings file now.
* User can manipulate the settings via the `global` commands.
* Syntax highlighting.
* The `up show` modificator for the TUI part.
* Fixed bug with double rows of the TreeView in the open dialog.
* Fixed bug with the Input in the open dialog, it supports Enter hit now.
* Fixed bug with ths save modificator, it does not crash the app now.
2 changes: 1 addition & 1 deletion thymus/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.1.1-alpha'
__version__ = '0.1.2-alpha'
4 changes: 2 additions & 2 deletions thymus/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sys

from . import __version__ as appver
from . import __version__ as app_ver
from . import tuier
from . import clier

Expand Down Expand Up @@ -37,7 +37,7 @@ def main(args: list[str]) -> None:
elif args[1] == 'tuier':
run_tui()
elif args[1] == 'version' or args[1] == 'ver':
print(f'Thymus ver. {appver}')
print(f'Thymus ver. {app_ver}')
else:
help()
else:
Expand Down
151 changes: 151 additions & 0 deletions thymus/app_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
from __future__ import annotations

import os
import sys
import json

from typing import TYPE_CHECKING, Any
from collections import deque
from dataclasses import dataclass

from pygments.styles import get_all_styles


if TYPE_CHECKING:

if sys.version_info.major == 3 and sys.version_info.minor >= 9:
from collections.abc import Callable, Iterable
else:
from typing import Callable, Iterable

CONFIG_PATH = 'thymus/settings'
SAVES_PATH = 'thymus/saves'
CONFIG_NAME = 'global.json'
MIN_CODE_WIDTH = 1500
MAX_CODE_WIDTH = 3000
DEFAULT_THEME = 'monokai'


@dataclass
class SettingsResponse:
status: str # error or success
value: 'Iterable[str]'

class AppSettings:
__slots__ = (
'__code_width',
'__theme',
'__errors',
'__is_dir',
)

@property
def code_width(self) -> int:
return self.__code_width

@property
def theme(self) -> str:
return self.__theme

@property
def styles(self) -> list[str]:
return get_all_styles()

@code_width.setter
def code_width(self, value: int) -> None:
if type(value) is not int or value > MAX_CODE_WIDTH or value < MIN_CODE_WIDTH:
raise ValueError(f'Code width must be >= {MIN_CODE_WIDTH} and <= {MAX_CODE_WIDTH}.')
self.__code_width = value

@theme.setter
def theme(self, value: str) -> None:
if value not in self.styles:
raise ValueError('Unknown theme.')
self.__theme = value

def __init__(self) -> None:
self.__code_width: int = MIN_CODE_WIDTH
self.__theme: str = DEFAULT_THEME
self.__errors: deque[str] = deque()
self.__is_dir: bool = True
self.__load_configuration()

def __load_configuration(self) -> None:
try:
if not os.path.exists(SAVES_PATH):
os.mkdir(SAVES_PATH)
if not os.path.exists(CONFIG_PATH):
os.mkdir(CONFIG_PATH)
else:
if not os.path.isdir(CONFIG_PATH):
self.__errors.append(f'The directory "{CONFIG_PATH}" cannot be created.')
self.__is_dir = False
return
if not os.path.exists(f'{CONFIG_PATH}/{CONFIG_NAME}'):
self.__save_settings()
else:
with open(f'{CONFIG_PATH}/{CONFIG_NAME}', encoding='utf-8') as f:
data: dict[str, Any] = json.load(f)
self.theme = data['theme']
self.code_width = int(data['code_width'])
except Exception as err:
if len(err.args):
err_line = ' '.join(err.args)
self.__errors.append(f'Something went wrong: "{err_line}".')

def __save_settings(self) -> None:
if not self.__is_dir:
return
with open(f'{CONFIG_PATH}/{CONFIG_NAME}', 'w', encoding='utf-8') as f:
data = {
'code_width': self.code_width,
'theme': self.theme,
}
json.dump(data, f)
f.flush()
os.fsync(f.fileno())

def playback(self, logger: 'Callable[[str], None]') -> None:
while self.__errors:
error = self.__errors.popleft()
logger(error)

def process_command(self, command: str) -> SettingsResponse:
if not command.startswith('global '):
return SettingsResponse('error', iter(['Unknown global command.']))
parts = command.split()
if len(parts) < 3:
return SettingsResponse('error', iter(['Incomplete global command.']))
subcommand = parts[1]
try:
if subcommand == 'show':
if len(parts) > 3:
return SettingsResponse('error', iter(['Too many arguments for `global show` command.']))
arg = parts[2]
if arg == 'themes':
result: list[str] = []
result.append('* -- current theme')
for theme in self.styles:
result.append(f'{theme}*' if theme == self.theme else theme)
return SettingsResponse('success', iter(result))
else:
return SettingsResponse('error', iter(['Unknown argument for `global show` command.']))
elif subcommand == 'set':
if len(parts) < 4:
return SettingsResponse('error', iter(['Incomplete `global set` command.']))
arg = parts[2]
if arg == 'theme':
if len(parts) > 4:
return SettingsResponse('error', iter(['Too many arguments for `global set` command.']))
value = parts[3]
self.theme = value
self.__save_settings()
return SettingsResponse('success', iter([f'+ The theme was changed to {self.theme}.'])) # TEMP
else:
return SettingsResponse('error', iter(['Unknown argument for `global set` command.']))
else:
return SettingsResponse('error', iter(['Unknown global command.']))
except ValueError as err:
if len(err.args):
return SettingsResponse('error', iter(err.args))
return SettingsResponse('error', [])
4 changes: 2 additions & 2 deletions thymus/contexts/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
__all__ = (
'Context',
'ContextResponse',
'JunosContext',
)

__version__ = '0.1.0'

from .context import (
Context,
ContextResponse,
JunosContext,
)
Loading

0 comments on commit 9f165a3

Please sign in to comment.