pywebexec 1.1.17__tar.gz → 1.1.19__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.
Files changed (27) hide show
  1. {pywebexec-1.1.17/pywebexec.egg-info → pywebexec-1.1.19}/PKG-INFO +1 -1
  2. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/pywebexec.py +34 -5
  3. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/css/style.css +2 -1
  4. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/js/script.js +1 -0
  5. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/version.py +2 -2
  6. {pywebexec-1.1.17 → pywebexec-1.1.19/pywebexec.egg-info}/PKG-INFO +1 -1
  7. {pywebexec-1.1.17 → pywebexec-1.1.19}/.github/workflows/python-publish.yml +0 -0
  8. {pywebexec-1.1.17 → pywebexec-1.1.19}/.gitignore +0 -0
  9. {pywebexec-1.1.17 → pywebexec-1.1.19}/LICENSE +0 -0
  10. {pywebexec-1.1.17 → pywebexec-1.1.19}/README.md +0 -0
  11. {pywebexec-1.1.17 → pywebexec-1.1.19}/pyproject.toml +0 -0
  12. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/__init__.py +0 -0
  13. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/images/aborted.svg +0 -0
  14. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/images/copy.svg +0 -0
  15. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/images/copy_ok.svg +0 -0
  16. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/images/failed.svg +0 -0
  17. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/images/favicon.svg +0 -0
  18. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/images/running.gif +0 -0
  19. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/static/images/success.svg +0 -0
  20. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/templates/__init__.py +0 -0
  21. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec/templates/index.html +0 -0
  22. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec.egg-info/SOURCES.txt +0 -0
  23. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec.egg-info/dependency_links.txt +0 -0
  24. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec.egg-info/entry_points.txt +0 -0
  25. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec.egg-info/requires.txt +0 -0
  26. {pywebexec-1.1.17 → pywebexec-1.1.19}/pywebexec.egg-info/top_level.txt +0 -0
  27. {pywebexec-1.1.17 → pywebexec-1.1.19}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.1.17
3
+ Version: 1.1.19
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -15,6 +15,7 @@ from gunicorn.app.base import Application
15
15
  import ipaddress
16
16
  from socket import gethostname, gethostbyname_ex
17
17
  import ssl
18
+ import re
18
19
  if os.environ.get('PYWEBEXEC_LDAP_SERVER'):
19
20
  from ldap3 import Server, Connection, ALL, SIMPLE, SUBTREE, Tls
20
21
 
@@ -136,10 +137,28 @@ class StandaloneApplication(Application):
136
137
  return self.application
137
138
 
138
139
 
140
+ def strip_ansi_control_chars(text):
141
+ """Remove ANSI and control characters from the text."""
142
+ # To clean
143
+ # ansi_escape = re.compile(r'''
144
+ # (?:\x1B[@-_]| # ANSI ESCape sequences
145
+ # \x1B\[.*?[ -/]*[@-~]| # ANSI CSI sequences
146
+ # \x1B\].*?\x07| # ANSI OSC sequences
147
+ # \x1B=P| # ANSI DCS sequences
148
+ # \x1B\\| # ANSI ST sequences
149
+ # \x1B\^| # ANSI PM sequences
150
+ # \x1B_.*?\x1B\\| # ANSI APC sequences
151
+ # [\x00-\x1F\x7F]) # Control characters
152
+ # ''', re.VERBOSE)
153
+ # ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|[(]B)|>')
154
+ ansi_escape = re.compile(br'(?:\x1B[@-Z\\-_]|[\x80-\x9A\x9C-\x9F]|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~]|\x1B([(]B|>))')
155
+ return ansi_escape.sub(b'', text)
156
+
157
+
139
158
  def decode_line(line: bytes) -> str:
140
159
  """try decode line exception on binary"""
141
160
  try:
142
- return line.decode()
161
+ return strip_ansi_control_chars(line).decode().strip(" ")
143
162
  except UnicodeDecodeError:
144
163
  return ""
145
164
 
@@ -149,17 +168,21 @@ def last_line(fd, maxline=1000):
149
168
  line = "\n"
150
169
  fd.seek(0, os.SEEK_END)
151
170
  size = 0
152
- while line in ["\n", "\r"] and size < maxline:
171
+ last_pos = 0
172
+ while line in ["", "\n", "\r"] and size < maxline:
153
173
  try: # catch if file empty / only empty lines
174
+ if last_pos:
175
+ fd.seek(last_pos-2, os.SEEK_SET)
154
176
  while fd.read(1) not in [b"\n", b"\r"]:
155
177
  fd.seek(-2, os.SEEK_CUR)
156
178
  size += 1
179
+ print(size)
157
180
  except OSError:
158
181
  fd.seek(0)
159
182
  line = decode_line(fd.readline())
160
183
  break
184
+ last_pos = fd.tell()
161
185
  line = decode_line(fd.readline())
162
- fd.seek(-4, os.SEEK_CUR)
163
186
  return line.strip()
164
187
 
165
188
 
@@ -317,6 +340,7 @@ def update_command_status(command_id, status, command=None, params=None, start_t
317
340
  if status != 'running':
318
341
  output_file_path = get_output_file_path(command_id)
319
342
  if os.path.exists(output_file_path):
343
+ print(output_file_path)
320
344
  status_data['last_output_line'] = get_last_non_empty_line_of_file(output_file_path)
321
345
  with open(status_file_path, 'w') as f:
322
346
  json.dump(status_data, f)
@@ -474,7 +498,7 @@ def stop_command(command_id):
474
498
  end_time = datetime.now().isoformat()
475
499
  try:
476
500
  os.kill(pid, 15) # Send SIGTERM
477
- update_command_status(command_id, 'aborted', end_time=end_time, exit_code=-15)
501
+ #update_command_status(command_id, 'aborted', end_time=end_time, exit_code=-15)
478
502
  return jsonify({'message': 'Command aborted'})
479
503
  except Exception as e:
480
504
  status_data = read_command_status(command_id) or {}
@@ -518,6 +542,11 @@ def list_commands():
518
542
  except AttributeError:
519
543
  params = " ".join([shlex.quote(p) if " " in p else p for p in status['params']])
520
544
  command = status.get('command', '-') + ' ' + params
545
+ last_line = status.get('last_output_line')
546
+ if last_line is None:
547
+ output_file_path = get_output_file_path(command_id)
548
+ if os.path.exists(output_file_path):
549
+ last_line = get_last_non_empty_line_of_file(output_file_path)
521
550
  commands.append({
522
551
  'command_id': command_id,
523
552
  'status': status['status'],
@@ -525,7 +554,7 @@ def list_commands():
525
554
  'end_time': status.get('end_time', 'N/A'),
526
555
  'command': command,
527
556
  'exit_code': status.get('exit_code', 'N/A'),
528
- 'last_output_line': status.get('last_output_line', get_last_non_empty_line_of_file(get_output_file_path(command_id))),
557
+ 'last_output_line': last_line,
529
558
  })
530
559
  # Sort commands by start_time in descending order
531
560
  commands.sort(key=lambda x: x['start_time'], reverse=True)
@@ -74,7 +74,8 @@ form {
74
74
  .title-icon {
75
75
  width: 30px;
76
76
  height: 30px;
77
- background-image: url("/static/images/favicon.svg")
77
+ background-image: url("/static/images/favicon.svg");
78
+ vertical-align: bottom;
78
79
  }
79
80
  .status-running {
80
81
  background-image: url("/static/images/running.gif")
@@ -17,6 +17,7 @@ document.getElementById('launchForm').addEventListener('submit', async (event) =
17
17
  throw new Error('Failed to launch command');
18
18
  }
19
19
  const data = await response.json();
20
+ await new Promise(r => setTimeout(r, 200));
20
21
  fetchCommands();
21
22
  viewOutput(data.command_id);
22
23
  } catch (error) {
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.1.17'
16
- __version_tuple__ = version_tuple = (1, 1, 17)
15
+ __version__ = version = '1.1.19'
16
+ __version_tuple__ = version_tuple = (1, 1, 19)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.1.17
3
+ Version: 1.1.19
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
File without changes
File without changes
File without changes
File without changes
File without changes