pywebexec 1.6.11__py3-none-any.whl → 1.6.13__py3-none-any.whl

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.
pywebexec/pywebexec.py CHANGED
@@ -25,7 +25,7 @@ import termios
25
25
  import struct
26
26
  import subprocess
27
27
  import logging
28
-
28
+ import pyte
29
29
 
30
30
  if os.environ.get('PYWEBEXEC_LDAP_SERVER'):
31
31
  from ldap3 import Server, Connection, ALL, SIMPLE, SUBTREE, Tls
@@ -181,8 +181,14 @@ class PyWebExec(Application):
181
181
  def load(self):
182
182
  return self.application
183
183
 
184
-
185
- ANSI_ESCAPE = re.compile(br'(?:\x1B[@-Z\\-_]|\x1B([(]B|>)|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~]|\x1B\[[0-9]{1,2};[0-9]{1,2}[m|K]|\x1B\[[0-9;]*[mGKHF]|[\x00-\x1F\x7F])')
184
+ def get_visible_output(line):
185
+ screen = pyte.Screen(len(line)+1, 2)
186
+ stream = pyte.Stream(screen)
187
+ stream.feed(line)
188
+ visible_line = screen.display[0]
189
+ return visible_line
190
+ #38;2;66;59;165m
191
+ ANSI_ESCAPE = re.compile(br'(?:\x1B[@-Z\\-_]|\x1B([(]B|>)|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~]|\x1B\[([0-9]{1,2};){0,4}[0-9]{1,3}[m|K]|\x1B\[[0-9;]*[mGKHF]|[\x00-\x1F\x7F])')
186
192
 
187
193
  def strip_ansi_control_chars(text):
188
194
  """Remove ANSI and control characters from the text."""
@@ -192,37 +198,27 @@ def strip_ansi_control_chars(text):
192
198
  def decode_line(line: bytes) -> str:
193
199
  """try decode line exception on binary"""
194
200
  try:
195
- return strip_ansi_control_chars(line).decode().strip(" ")
201
+ return get_visible_output(line.decode()).strip(" ")
196
202
  except UnicodeDecodeError:
197
203
  return ""
198
204
 
199
205
 
200
- def last_line(fd, maxsize=500):
201
- """last non empty line of file"""
202
- line = "\n"
203
- fd.seek(0, os.SEEK_END)
204
- size = 0
205
- last_pos = 0
206
- while line in ["", "\n", "\r"] and size < maxsize:
207
- try: # catch if file empty / only empty lines
208
- if last_pos:
209
- fd.seek(last_pos-2, os.SEEK_SET)
210
- while fd.read(1) not in [b"\n", b"\r"] and size < maxsize:
211
- fd.seek(-2, os.SEEK_CUR)
212
- size += 1
206
+ def get_last_line(file_path, maxsize=1024):
207
+ """Retrieve last non empty line after vt100 interpretation"""
208
+ with open(file_path, 'rb') as fd:
209
+ try:
210
+ fd.seek(-maxsize, os.SEEK_END)
213
211
  except OSError:
214
212
  fd.seek(0)
215
- line = decode_line(fd.readline())
216
- break
217
- last_pos = fd.tell()
218
- line = decode_line(fd.readline())
219
- return line.strip()
220
-
221
-
222
- def get_last_non_empty_line_of_file(file_path):
223
- """Get the last non-empty line of a file."""
224
- with open(file_path, 'rb') as f:
225
- return last_line(f)
213
+ lines = fd.read().split(b"\n")
214
+ if len(lines) == 1:
215
+ return ""
216
+ line = ""
217
+ while True:
218
+ line = decode_line(lines.pop())
219
+ if line or not lines:
220
+ break
221
+ return line
226
222
 
227
223
 
228
224
  def start_gunicorn(daemonized=False, baselog=None):
@@ -435,7 +431,7 @@ def update_command_status(command_id, updates):
435
431
  if status_data['status'] != 'running':
436
432
  output_file_path = get_output_file_path(command_id)
437
433
  if os.path.exists(output_file_path):
438
- status_data['last_output_line'] = get_last_non_empty_line_of_file(output_file_path)
434
+ status_data['last_output_line'] = get_last_line(output_file_path)
439
435
  with open(status_file_path, 'w') as f:
440
436
  json.dump(status_data, f)
441
437
 
@@ -577,7 +573,7 @@ def read_commands():
577
573
  if last_line is None:
578
574
  output_file_path = get_output_file_path(command_id)
579
575
  if os.path.exists(output_file_path):
580
- last_line = get_last_non_empty_line_of_file(output_file_path)
576
+ last_line = get_last_line(output_file_path)
581
577
  commands.append({
582
578
  'command_id': command_id,
583
579
  'status': status['status'],
pywebexec/version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.6.11'
16
- __version_tuple__ = version_tuple = (1, 6, 11)
15
+ __version__ = version = '1.6.13'
16
+ __version_tuple__ = version_tuple = (1, 6, 13)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.6.11
3
+ Version: 1.6.13
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -59,6 +59,7 @@ Requires-Dist: Flask-HTTPAuth>=4.8.0
59
59
  Requires-Dist: pexpect>=4.9.0
60
60
  Requires-Dist: gunicorn>=21.2.0
61
61
  Requires-Dist: ldap3>=2.9.1
62
+ Requires-Dist: pyte>=0.8.1
62
63
 
63
64
  [![Pypi version](https://img.shields.io/pypi/v/pywebexec.svg)](https://pypi.org/project/pywebexec/)
64
65
  ![example](https://github.com/joknarf/pywebexec/actions/workflows/python-publish.yml/badge.svg)
@@ -1,6 +1,6 @@
1
1
  pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
2
- pywebexec/pywebexec.py,sha256=CLHDHDkgsftj8VWKp6ofnUgM8oQf8rvCvROlnljP4ow,32577
3
- pywebexec/version.py,sha256=fbMgF3ll0MwJN-g_yFfiW2LYnfAEawaw32LMtGtBPy8,413
2
+ pywebexec/pywebexec.py,sha256=hE7O0lZgg_nMk1VIFlBUv6lJ6T5sJFFBycnDmVnRQg4,32518
3
+ pywebexec/version.py,sha256=F6yOQF2i7n8OMZVy5diTWAk2RoBPwyUW2P5AsaXwnHo,413
4
4
  pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
5
5
  pywebexec/static/css/style.css,sha256=MJHUBpjWL4sLxM7a7DxypmPKaFJQbmA_ESNXsbLviNI,8201
6
6
  pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
@@ -33,9 +33,9 @@ pywebexec/static/js/xterm/xterm.js.map,sha256=Y7O2Pb-fIS7Z8AC1D5s04_aiW_Jf1f4mCf
33
33
  pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  pywebexec/templates/index.html,sha256=5h8kLyzAhbvUDU9sEwrGIvD6FxAMcDZLXlN-ldlO8KU,2880
35
35
  pywebexec/templates/popup.html,sha256=3ZqQcE9mYs-RXv0Lfb24zntOlvR137ZYI9mtCZNVAo0,1407
36
- pywebexec-1.6.11.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
37
- pywebexec-1.6.11.dist-info/METADATA,sha256=Do6KMuv1dJL4UjCD-ypdImK1RMOxiGRBnDC0u6uNPoM,7974
38
- pywebexec-1.6.11.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
39
- pywebexec-1.6.11.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
40
- pywebexec-1.6.11.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
41
- pywebexec-1.6.11.dist-info/RECORD,,
36
+ pywebexec-1.6.13.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
37
+ pywebexec-1.6.13.dist-info/METADATA,sha256=zXXihO8zyUycPpw9EBZK_YbKa9-P4PcHR-RPPPSQ-Qc,8001
38
+ pywebexec-1.6.13.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
39
+ pywebexec-1.6.13.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
40
+ pywebexec-1.6.13.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
41
+ pywebexec-1.6.13.dist-info/RECORD,,