"""
PyDetex
https://github.com/ppizarror/PyDetex
UTILS
Module that contain all util methods and classes used in parsers and pipelines,
from tex, language, and low-level.
"""
__all__ = [
'apply_tag_between_inside_char_command',
'apply_tag_tex_commands',
'apply_tag_tex_commands_no_argv',
'Button',
'check_repeated_words',
'complete_langs_dict',
'detect_language',
'find_tex_command_char',
'find_tex_commands',
'find_tex_commands_noargv',
'find_tex_environments',
'format_number_d',
'get_diff_startend_word',
'get_language_name',
'get_local_path',
'get_number_of_day',
'get_tex_commands_args',
'get_word_from_cursor',
'IS_OSX',
'LangTexTextTags',
'make_stemmer',
'open_file',
'ProgressBar',
'RESOURCES_PATH',
'split_tags',
'syntax_highlight',
'TEX_COMMAND_CHARS',
'TEX_EQUATION_CHARS',
'tex_to_unicode',
'tokenize',
'validate_float',
'validate_int'
]
import datetime
import os
import platform
import sys
import time
from pathlib import Path
from typing import List, Tuple, Dict
from pydetex._fonts import FONT_TAGS as _FONT_TAGS
from pydetex._utils_lang import *
from pydetex._utils_tex import *
# Resources path
__actualpath = str(os.path.abspath(os.path.dirname(__file__))).replace('\\', '/') + '/'
RESOURCES_PATH = __actualpath + 'res/'
# Check OS
IS_OSX = platform.system() == 'Darwin'
# Import Button widget
if IS_OSX:
from tkmacosx import Button
else:
from tkinter import Button
def button_text(s: str) -> str:
"""
Generates the button text.
:param s: Button's text
:return: Text
"""
return s if IS_OSX else f' {s} '
[docs]def validate_int(p: str) -> bool:
"""
Validate an integer.
:param p: Value
:return: True if integer
"""
if p == '' or p == '-':
return True
try:
p = float(p)
return int(p) == p
except ValueError:
pass
return False
[docs]def validate_float(p: str) -> bool:
"""
Validate a float.
:param p: Value
:return: True if integer
"""
if p == '' or p == '-':
return True
try:
float(p)
return True
except ValueError:
pass
return False
[docs]def syntax_highlight(s: str) -> str:
"""
Syntax highlighter.
:param s: Latex string code
:return: Code with format
"""
# Add initial normal
s = _FONT_TAGS['normal'] + s.strip()
# Format equations
s = apply_tag_between_inside_char_command(
s=s,
symbols_char=TEX_EQUATION_CHARS,
tags=(_FONT_TAGS['equation_char'], _FONT_TAGS['equation_inside'],
_FONT_TAGS['equation_char'], _FONT_TAGS['normal'])
)
# Format commands with {arguments}
s = apply_tag_tex_commands(
s=s,
tags=(_FONT_TAGS['tex_command'],
_FONT_TAGS['normal'],
_FONT_TAGS['tex_argument'],
_FONT_TAGS['normal'],
'')
)
# Format commands without arguments
s = apply_tag_tex_commands_no_argv(
s=s,
tags=(_FONT_TAGS['tex_command'], _FONT_TAGS['normal'])
)
# Return formatted string
return s
[docs]def get_number_of_day() -> int:
"""
Return the number of the day from the current year.
:return: Day number
"""
return datetime.datetime.now().timetuple().tm_yday
[docs]def open_file(f: str) -> str:
"""
Open file and return its string.
:param f: Filename
:return: File content
"""
o = open(f, encoding='utf-8')
text = ''.join(o.readlines())
o.close()
return text
def make_path_if_not_exists(path: str) -> str:
"""
Create path if not exists.
:param path: Path
:return: Path
"""
if not os.path.isdir(path):
Path(path).mkdir(parents=True, exist_ok=True)
return path
[docs]def get_local_path() -> str:
"""
:return: Returns the app local path
"""
appdata = os.getenv('LOCALAPPDATA')
if appdata is None:
appdata = os.path.join(get_user_path(), 'Applications')
path = os.path.join(appdata, 'PyDetex')
return make_path_if_not_exists(path)
def get_user_path() -> str:
"""
:return: Returns the user path
"""
return os.path.expanduser('~')
[docs]class ProgressBar(object):
"""
Basic progress bar implementation.
"""
_current: int
_last_step: float
_size: int
_step_times: Dict[str, float]
_steps: int
_t0: float
def __init__(self, steps: int, size: int = 15) -> None:
"""
Constructor.
:param steps: How many steps have the procedure
:param size: Bar size
"""
assert isinstance(steps, int) and steps >= 1
assert isinstance(size, int) and size >= 1
self._current = 0
self._last_step = time.time()
self._size = size # Bar size
self._step_times = {}
self._steps = steps - 1
self._t0 = time.time()
def _print_progress_bar(self, i: int, max_: int, post_text: str) -> None:
"""
Prints a progress bar.
:param i: Progress bar
:param max_: Max steps
:param post_text: Status
"""
j = i / max_
sys.stdout.write('\r')
sys.stdout.write(f"[{'=' * int(self._size * j):{self._size}s}] {int(100 * j)}% {post_text}")
sys.stdout.flush()
[docs] def update(self, status: str = '', print_total_time: bool = True) -> None:
"""
Update the current status to a new step.
:param status: Status text
:param print_total_time: Prints total computing time
"""
if self._current > self._steps:
return
self._print_progress_bar(self._current, self._steps, status)
dt = time.time() - self._last_step
self._last_step = time.time()
self._step_times[status] = dt
self._current += 1
if self._current == self._steps + 1:
print('')
sys.stdout.flush()
if print_total_time:
print(f'Process finished in {time.time() - self._t0:.3f} seconds')
[docs] def detail_times(self) -> None:
"""
Print times.
"""
for k in self._step_times.keys():
print(f'{self._step_times[k]:.3f}s\t{k}')
[docs] def reset(self) -> None:
"""
Reset the steps.
"""
self._current = 0
self._t0 = time.time()
self._last_step = time.time()
self._step_times.clear()