Skip to content

Commit 1dbedb8

Browse files
szachovyCopilot
andcommitted
Upload .py source instead of .pyc bytecode for Python version robustness
Replace the hardcoded Python 3.10 .pyc magic number and bytecode compilation with plain .py source upload. The remote node's Python compiles at runtime, decoupling the host Python version from the container Python version. - Remove compile()/marshal/.pyc header from run_python_container_command, upload .py source and execute via python3 - Remove unused marshal import from remote.py - Add minimum version guard (>= 3.10) in initialize.py - Update README to require Python >= 3.10 on the host - Update ARCHITECTURE.md to reflect source execution model Closes #38 Closes #41 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0ab8ce7 commit 1dbedb8

5 files changed

Lines changed: 14 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2424

2525
### Fixed
2626

27+
* Upload `.py` source instead of `.pyc` bytecode to decouple host Python version. (#38, #41)
2728
* Fixed `run_mysql_server()` not instantiating `MySQLServer` class. (#94)
2829
* Disabled MD060 markdownlint rule to fix table column style false positives in documentation. (#94)
2930

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Follow [ARCHITECTURE.md](docs/ARCHITECTURE.md) for more.
3232

3333
The following software needs to be installed on the user's host:
3434

35-
* `python v3.10.12` with the following third party packages:
35+
* `python >= 3.10` with the following third party packages:
3636
* `paramiko v3.5.0`
3737

3838
The following software needs to be installed on the external nodes:

docs/ARCHITECTURE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ The controller runs on the user's workstation and communicates with cluster node
114114
[Paramiko](https://www.paramiko.org/). The `RemoteConnection` class provides:
115115

116116
* **SSH/SFTP connections**: connects as the `superset` user, either directly or through `~/.ssh/config`.
117-
* **Python bytecode execution**: `container.py` source is compiled to `.pyc`, uploaded to the remote node,
118-
and executed via `python3 /opt/<nonce>.pyc`. This allows running Docker API commands on remote nodes
119-
without installing additional management software.
117+
* **Python source execution**: `container.py` source is uploaded to the remote node along with the
118+
command to execute, and run via `python3 /opt/<nonce>.py`. This allows running Docker API commands on
119+
remote nodes without installing additional management software.
120120
* **Directory and file uploads**: recursive SFTP-based uploads of service directories, certificates,
121121
and passwords.
122122

src/initialize.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,17 @@
4848

4949
# pylint: disable=consider-using-f-string
5050
# pylint: disable=attribute-defined-outside-init
51+
# pylint: disable=wrong-import-position
52+
53+
import sys
54+
55+
if sys.version_info < (3, 10):
56+
sys.exit(f"Python >= 3.10 required (found {sys.version_info.major}.{sys.version_info.minor})")
5157

5258
import base64
5359
import functools
5460
import ipaddress
5561
import itertools
56-
import sys
5762
import re
5863
import socket
5964

src/remote.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import functools
4040
import io
4141
import logging
42-
import marshal
4342
import os
4443
import pathlib
4544
import random
@@ -124,12 +123,9 @@ def run_python_container_command(self, command: str) -> dict:
124123
mode="r",
125124
encoding="utf-8"
126125
) as memfile:
127-
code_object = compile(memfile.read() + command, filename=nonce, mode="exec")
128-
pyc_file = io.BytesIO()
129-
pyc_file.write(b'o\r\r\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
130-
marshal.dump(code_object, pyc_file)
131-
self.upload_file(content=pyc_file.getvalue(), remote_file_path=f'/opt/{nonce}.pyc')
132-
_, stdout, stderr = self.ssh_client.exec_command(f"python3 /opt/{nonce}.pyc")
126+
source = memfile.read() + command
127+
self.upload_file(content=source, remote_file_path=f'/opt/{nonce}.py')
128+
_, stdout, stderr = self.ssh_client.exec_command(f"python3 /opt/{nonce}.py")
133129
return {
134130
"output": stdout.read().decode(),
135131
"error": stderr.read().decode()

0 commit comments

Comments
 (0)