pyfernet-payload 0.2.4__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 shunyaoyin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyfernet-payload
3
+ Version: 0.2.4
4
+ Summary: Encrypt a Python source directory into one .enc file and run it from memory (Fernet AES, stdlib-only).
5
+ Home-page: https://github.com/yinshunyao/PyFernet
6
+ Author: shunyaoyin
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/yinshunyao/PyFernet
9
+ Project-URL: Issues, https://github.com/yinshunyao/PyFernet/issues
10
+ Project-URL: Documentation, https://github.com/yinshunyao/PyFernet#readme
11
+ Keywords: fernet,aes,encrypt,source-protection,in-memory,training
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Security :: Cryptography
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Dynamic: home-page
29
+ Dynamic: license-file
30
+ Dynamic: requires-python
31
+
32
+ # PyFernet (`pyfernet-payload`)
33
+
34
+ [中文说明](README.zh-CN.md)
35
+
36
+ Pack a training source directory into a single `.enc` blob; on the client, decrypt and run from an **in-memory VFS**. Only an **empty directory tree** is created on disk (for `Path(__file__)` / `chdir`); `.py` / config **contents are never written**. Removed on exit.
37
+
38
+ - **Zero third-party runtime deps** (stdlib AES-128-CBC + Fernet-compatible format)
39
+ - **PyPI name**: `pyfernet-payload` (`pyfernet` is already taken)
40
+ - **CLI / import**: `pyfernet` / `import pyfernet`
41
+ - **Relative paths work**: `Path(__file__).parent / "train_config.json"`, `open`, `spec_from_file_location` via VFS hooks
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install pyfernet-payload
47
+ ```
48
+
49
+ From a local clone:
50
+
51
+ ```bash
52
+ # preferred on older pip / mirrors (no editable hook needed)
53
+ python3 -m pip install .
54
+
55
+ # or editable (dev); if this fails, upgrade pip first:
56
+ python3 -m pip install -U pip setuptools wheel
57
+ python3 -m pip install -e .
58
+ ```
59
+ ## CLI
60
+
61
+ ### Encrypt
62
+
63
+ ```bash
64
+ pyfernet encrypt ./my_train -o train_payload.enc -e train.py
65
+ # passphrase via prompt; or:
66
+ export PYFERNET_PASSWORD='your-secret'
67
+ pyfernet encrypt ./my_train -o train_payload.enc -e train.py --password-env PYFERNET_PASSWORD
68
+ ```
69
+
70
+ ### Run
71
+
72
+ ```bash
73
+ pyfernet run train_payload.enc
74
+ # forward args to the training entry (after --):
75
+ pyfernet run train_payload.enc -- --epochs 50 --batch 8
76
+ ```
77
+
78
+ Equivalent:
79
+
80
+ ```bash
81
+ python -m pyfernet encrypt ./my_train -o train_payload.enc -e train.py
82
+ python -m pyfernet run train_payload.enc
83
+ ```
84
+
85
+ ## Workflow
86
+
87
+ 1. **Local**: write training code → `pyfernet encrypt` → `train_payload.enc`
88
+ 2. **SFTP**: upload the ciphertext only (client already has `pip install pyfernet-payload`)
89
+ 3. **SSH**: `pyfernet run train_payload.enc`, enter passphrase
90
+ 4. Decrypt into memory → empty dir anchors + VFS hooks → start training; write weights to a normal disk path
91
+ 5. On exit, remove empty dirs; durable disk still has only ciphertext
92
+
93
+ If the entry uses sibling dirs (e.g. `train_detect_rtdetrv2/` + `train_detect_cfg/` + `train_detect_yolo/`), **encrypt the parent folder** and set `-e train_detect_rtdetrv2/train_insect.py`.
94
+
95
+ Inference code, datasets, and weights can stay plaintext on the client.
96
+
97
+ ## IDE variable mode (optional)
98
+
99
+ With no CLI args, modules use the variables at the bottom of the file:
100
+
101
+ ```bash
102
+ python -m pyfernet.encryptor # no args → SOURCE_DIR / OUTPUT_PATH / ENTRY_POINT
103
+ python -m pyfernet.loader # no args → PAYLOAD_PATH / TRAIN_ARGV
104
+ ```
105
+
106
+ With args, they use the CLI, e.g. `python -m pyfernet.encryptor ./src -o out.enc -e train.py`.
107
+
108
+ ## Library API
109
+
110
+ ```python
111
+ from pyfernet import encrypt_directory, run_payload
112
+
113
+ encrypt_directory("examples/demo_train", "dist/train_payload.enc", "train.py", "secret")
114
+ run_payload("dist/train_payload.enc", "secret", argv=["train.py", "--epochs", "1"])
115
+ ```
116
+
117
+ ## Payload format
118
+
119
+ ```text
120
+ PYFE1\0 + 16B salt + Fernet(token)
121
+ ```
122
+
123
+ Fernet plaintext is a zip (sources + `_pyfernet_manifest.json`). The passphrase is stretched with PBKDF2-HMAC-SHA256 (390000 iterations).
124
+
125
+ ## Notes
126
+
127
+ - Source file **bytes never hit disk**; only empty directories exist briefly (often under `/dev/shm` on Linux)
128
+ - Process memory / debuggers can still see plaintext; a leaked passphrase decrypts the blob
129
+ - By default only `.py` and a few config suffixes are packed—do not put datasets or weights in the blob
130
+ - The client still needs training deps installed (torch, ultralytics, etc.)
131
+
132
+ ## Publish to PyPI
133
+
134
+ See [docs/PUBLISH.md](docs/PUBLISH.md) ([中文](docs/PUBLISH.zh-CN.md)).
135
+
136
+ ## Layout
137
+
138
+ ```text
139
+ PyFernet/
140
+ pyproject.toml
141
+ README.md # English (PyPI)
142
+ README.zh-CN.md # Chinese
143
+ src/pyfernet/
144
+ cli.py
145
+ encryptor.py
146
+ loader.py
147
+ fernet_lite.py
148
+ examples/demo_train/
149
+ docs/PUBLISH.md
150
+ docs/PUBLISH.zh-CN.md
151
+ ```
@@ -0,0 +1,120 @@
1
+ # PyFernet (`pyfernet-payload`)
2
+
3
+ [中文说明](README.zh-CN.md)
4
+
5
+ Pack a training source directory into a single `.enc` blob; on the client, decrypt and run from an **in-memory VFS**. Only an **empty directory tree** is created on disk (for `Path(__file__)` / `chdir`); `.py` / config **contents are never written**. Removed on exit.
6
+
7
+ - **Zero third-party runtime deps** (stdlib AES-128-CBC + Fernet-compatible format)
8
+ - **PyPI name**: `pyfernet-payload` (`pyfernet` is already taken)
9
+ - **CLI / import**: `pyfernet` / `import pyfernet`
10
+ - **Relative paths work**: `Path(__file__).parent / "train_config.json"`, `open`, `spec_from_file_location` via VFS hooks
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install pyfernet-payload
16
+ ```
17
+
18
+ From a local clone:
19
+
20
+ ```bash
21
+ # preferred on older pip / mirrors (no editable hook needed)
22
+ python3 -m pip install .
23
+
24
+ # or editable (dev); if this fails, upgrade pip first:
25
+ python3 -m pip install -U pip setuptools wheel
26
+ python3 -m pip install -e .
27
+ ```
28
+ ## CLI
29
+
30
+ ### Encrypt
31
+
32
+ ```bash
33
+ pyfernet encrypt ./my_train -o train_payload.enc -e train.py
34
+ # passphrase via prompt; or:
35
+ export PYFERNET_PASSWORD='your-secret'
36
+ pyfernet encrypt ./my_train -o train_payload.enc -e train.py --password-env PYFERNET_PASSWORD
37
+ ```
38
+
39
+ ### Run
40
+
41
+ ```bash
42
+ pyfernet run train_payload.enc
43
+ # forward args to the training entry (after --):
44
+ pyfernet run train_payload.enc -- --epochs 50 --batch 8
45
+ ```
46
+
47
+ Equivalent:
48
+
49
+ ```bash
50
+ python -m pyfernet encrypt ./my_train -o train_payload.enc -e train.py
51
+ python -m pyfernet run train_payload.enc
52
+ ```
53
+
54
+ ## Workflow
55
+
56
+ 1. **Local**: write training code → `pyfernet encrypt` → `train_payload.enc`
57
+ 2. **SFTP**: upload the ciphertext only (client already has `pip install pyfernet-payload`)
58
+ 3. **SSH**: `pyfernet run train_payload.enc`, enter passphrase
59
+ 4. Decrypt into memory → empty dir anchors + VFS hooks → start training; write weights to a normal disk path
60
+ 5. On exit, remove empty dirs; durable disk still has only ciphertext
61
+
62
+ If the entry uses sibling dirs (e.g. `train_detect_rtdetrv2/` + `train_detect_cfg/` + `train_detect_yolo/`), **encrypt the parent folder** and set `-e train_detect_rtdetrv2/train_insect.py`.
63
+
64
+ Inference code, datasets, and weights can stay plaintext on the client.
65
+
66
+ ## IDE variable mode (optional)
67
+
68
+ With no CLI args, modules use the variables at the bottom of the file:
69
+
70
+ ```bash
71
+ python -m pyfernet.encryptor # no args → SOURCE_DIR / OUTPUT_PATH / ENTRY_POINT
72
+ python -m pyfernet.loader # no args → PAYLOAD_PATH / TRAIN_ARGV
73
+ ```
74
+
75
+ With args, they use the CLI, e.g. `python -m pyfernet.encryptor ./src -o out.enc -e train.py`.
76
+
77
+ ## Library API
78
+
79
+ ```python
80
+ from pyfernet import encrypt_directory, run_payload
81
+
82
+ encrypt_directory("examples/demo_train", "dist/train_payload.enc", "train.py", "secret")
83
+ run_payload("dist/train_payload.enc", "secret", argv=["train.py", "--epochs", "1"])
84
+ ```
85
+
86
+ ## Payload format
87
+
88
+ ```text
89
+ PYFE1\0 + 16B salt + Fernet(token)
90
+ ```
91
+
92
+ Fernet plaintext is a zip (sources + `_pyfernet_manifest.json`). The passphrase is stretched with PBKDF2-HMAC-SHA256 (390000 iterations).
93
+
94
+ ## Notes
95
+
96
+ - Source file **bytes never hit disk**; only empty directories exist briefly (often under `/dev/shm` on Linux)
97
+ - Process memory / debuggers can still see plaintext; a leaked passphrase decrypts the blob
98
+ - By default only `.py` and a few config suffixes are packed—do not put datasets or weights in the blob
99
+ - The client still needs training deps installed (torch, ultralytics, etc.)
100
+
101
+ ## Publish to PyPI
102
+
103
+ See [docs/PUBLISH.md](docs/PUBLISH.md) ([中文](docs/PUBLISH.zh-CN.md)).
104
+
105
+ ## Layout
106
+
107
+ ```text
108
+ PyFernet/
109
+ pyproject.toml
110
+ README.md # English (PyPI)
111
+ README.zh-CN.md # Chinese
112
+ src/pyfernet/
113
+ cli.py
114
+ encryptor.py
115
+ loader.py
116
+ fernet_lite.py
117
+ examples/demo_train/
118
+ docs/PUBLISH.md
119
+ docs/PUBLISH.zh-CN.md
120
+ ```
@@ -0,0 +1,59 @@
1
+ [build-system]
2
+ # setuptools>=64 才有 PEP 660 editable;旧 pip 会回退读同目录 setup.py
3
+ requires = ["setuptools>=68", "wheel"]
4
+ build-backend = "setuptools.build_meta"
5
+
6
+ [project]
7
+ name = "pyfernet-payload"
8
+ version = "0.2.4"
9
+ description = "Encrypt a Python source directory into one .enc file and run it from memory (Fernet AES, stdlib-only)."
10
+ readme = "README.md"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ requires-python = ">=3.10"
14
+ authors = [
15
+ { name = "shunyaoyin" },
16
+ ]
17
+ keywords = [
18
+ "fernet",
19
+ "aes",
20
+ "encrypt",
21
+ "source-protection",
22
+ "in-memory",
23
+ "training",
24
+ ]
25
+ classifiers = [
26
+ "Development Status :: 4 - Beta",
27
+ "Environment :: Console",
28
+ "Intended Audience :: Developers",
29
+ "Operating System :: OS Independent",
30
+ "Programming Language :: Python :: 3",
31
+ "Programming Language :: Python :: 3 :: Only",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13",
36
+ "Topic :: Security :: Cryptography",
37
+ "Topic :: Software Development :: Libraries :: Python Modules",
38
+ "Typing :: Typed",
39
+ ]
40
+ # 零运行时第三方依赖(纯标准库)
41
+ dependencies = []
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/yinshunyao/PyFernet"
45
+ Issues = "https://github.com/yinshunyao/PyFernet/issues"
46
+ Documentation = "https://github.com/yinshunyao/PyFernet#readme"
47
+
48
+ [project.scripts]
49
+ pyfernet = "pyfernet.cli:main"
50
+
51
+ [tool.setuptools]
52
+ package-dir = { "" = "src" }
53
+
54
+ [tool.setuptools.packages.find]
55
+ where = ["src"]
56
+ include = ["pyfernet*"]
57
+
58
+ [tool.setuptools.package-data]
59
+ pyfernet = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env python3
2
+ """setuptools entry for older pip that does not fully honor pyproject [project].
3
+
4
+ Keeps name/version/entry points explicit so installs are not UNKNOWN-0.0.0.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ from pathlib import Path
11
+
12
+ from setuptools import find_packages, setup
13
+
14
+ ROOT = Path(__file__).resolve().parent
15
+ SRC = ROOT / "src"
16
+
17
+
18
+ def _version() -> str:
19
+ init = (SRC / "pyfernet" / "__init__.py").read_text(encoding="utf-8")
20
+ m = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']', init, re.M)
21
+ if not m:
22
+ raise RuntimeError("cannot find __version__ in pyfernet/__init__.py")
23
+ return m.group(1)
24
+
25
+
26
+ setup(
27
+ name="pyfernet-payload",
28
+ version=_version(),
29
+ description=(
30
+ "Encrypt a Python source directory into one .enc file "
31
+ "and run it from memory (Fernet AES, stdlib-only)."
32
+ ),
33
+ long_description=(ROOT / "README.md").read_text(encoding="utf-8"),
34
+ long_description_content_type="text/markdown",
35
+ author="shunyaoyin",
36
+ url="https://github.com/yinshunyao/PyFernet",
37
+ license="MIT",
38
+ package_dir={"": "src"},
39
+ packages=find_packages(where="src"),
40
+ package_data={"pyfernet": ["py.typed"]},
41
+ include_package_data=True,
42
+ python_requires=">=3.10",
43
+ install_requires=[],
44
+ entry_points={
45
+ "console_scripts": [
46
+ "pyfernet=pyfernet.cli:main",
47
+ ],
48
+ },
49
+ classifiers=[
50
+ "Development Status :: 4 - Beta",
51
+ "Environment :: Console",
52
+ "Intended Audience :: Developers",
53
+ "License :: OSI Approved :: MIT License",
54
+ "Operating System :: OS Independent",
55
+ "Programming Language :: Python :: 3",
56
+ "Programming Language :: Python :: 3 :: Only",
57
+ "Programming Language :: Python :: 3.10",
58
+ "Programming Language :: Python :: 3.11",
59
+ "Programming Language :: Python :: 3.12",
60
+ "Programming Language :: Python :: 3.13",
61
+ "Topic :: Security :: Cryptography",
62
+ ],
63
+ )
@@ -0,0 +1,14 @@
1
+ """PyFernet: encrypt a source directory into one .enc file and run it from memory."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __version__ = "0.2.4"
6
+
7
+ from pyfernet.encryptor import encrypt_directory
8
+ from pyfernet.loader import run_payload
9
+
10
+ __all__ = [
11
+ "__version__",
12
+ "encrypt_directory",
13
+ "run_payload",
14
+ ]
@@ -0,0 +1,8 @@
1
+ """python -m pyfernet → CLI。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pyfernet.cli import main
6
+
7
+ if __name__ == "__main__":
8
+ raise SystemExit(main())
@@ -0,0 +1,158 @@
1
+ """命令行入口:pyfernet encrypt | run"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import os
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from pyfernet import __version__
11
+ from pyfernet.encryptor import main as encrypt_main
12
+ from pyfernet.loader import list_payload, main as run_main
13
+
14
+
15
+ DEFAULT_PASSWORD_ENV = "PYFERNET_PASSWORD"
16
+
17
+
18
+ def _password_from_args(password: str | None, password_env: str | None) -> str | None:
19
+ """解析口令:-p > --password-env > 环境变量 PYFERNET_PASSWORD > None(交互)。"""
20
+ if password is not None:
21
+ return password
22
+ env_name = password_env or DEFAULT_PASSWORD_ENV
23
+ # 显式传了 --password-env,或默认名在环境里有值时,走环境变量
24
+ if password_env is not None or env_name in os.environ:
25
+ value = os.environ.get(env_name)
26
+ if not value:
27
+ raise SystemExit(
28
+ f"环境变量未设置或为空: {env_name}\n"
29
+ f" --password-env 后面是「变量名」不是口令本身。\n"
30
+ f" 正确示例:\n"
31
+ f" export {DEFAULT_PASSWORD_ENV}='你的口令'\n"
32
+ f" nohup pyfernet run train.enc --password-env {DEFAULT_PASSWORD_ENV} > d.log 2>&1 &\n"
33
+ f" 或(会进进程列表,仅临时用):\n"
34
+ f" nohup pyfernet run train.enc -p '你的口令' > d.log 2>&1 &"
35
+ )
36
+ return value
37
+ return None
38
+
39
+
40
+ def _split_script_args(argv: list[str]) -> tuple[list[str], list[str]]:
41
+ """将 `run ... -- script_args` 拆开,避免 REMAINDER 吞掉 -p/--password-env。"""
42
+ if argv and argv[0] == "run" and "--" in argv:
43
+ idx = argv.index("--")
44
+ return argv[:idx], argv[idx + 1 :]
45
+ return argv, []
46
+
47
+
48
+ def build_parser() -> argparse.ArgumentParser:
49
+ parser = argparse.ArgumentParser(
50
+ prog="pyfernet",
51
+ description="将源码目录加密为单个 .enc,并在内存中解密运行(磁盘无明文源码)。",
52
+ )
53
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
54
+ sub = parser.add_subparsers(dest="command", required=True)
55
+
56
+ p_enc = sub.add_parser("encrypt", help="打包并加密源码目录")
57
+ p_enc.add_argument("source_dir", type=Path, help="要加密的源码目录")
58
+ p_enc.add_argument(
59
+ "-o",
60
+ "--output",
61
+ type=Path,
62
+ default=Path("train_payload.enc"),
63
+ help="输出密文路径(默认: train_payload.enc)",
64
+ )
65
+ p_enc.add_argument(
66
+ "-e",
67
+ "--entry",
68
+ default="train.py",
69
+ help="相对源目录的入口脚本(默认: train.py)",
70
+ )
71
+ p_enc.add_argument(
72
+ "-p",
73
+ "--password",
74
+ default=None,
75
+ help="口令(不推荐写在命令行;默认交互输入)。也可用 --password-env",
76
+ )
77
+ p_enc.add_argument(
78
+ "--password-env",
79
+ default=None,
80
+ metavar="VAR",
81
+ help="从环境变量读口令(填变量名,如 PYFERNET_PASSWORD;不是口令本身)",
82
+ )
83
+
84
+ p_run = sub.add_parser(
85
+ "run",
86
+ help="解密密文包并在内存中执行",
87
+ epilog="传给训练脚本的参数写在 -- 之后,例如: pyfernet run a.enc -- --epochs 10",
88
+ )
89
+ p_run.add_argument("payload", type=Path, help="密文包路径(.enc)")
90
+ p_run.add_argument(
91
+ "-p",
92
+ "--password",
93
+ default=None,
94
+ help="口令(不推荐写在命令行;默认交互输入)。也可用 --password-env",
95
+ )
96
+ p_run.add_argument(
97
+ "--password-env",
98
+ default=None,
99
+ metavar="VAR",
100
+ help="从环境变量读口令(填变量名,如 PYFERNET_PASSWORD;不是口令本身)",
101
+ )
102
+
103
+ p_list = sub.add_parser("list", help="列出密文包内文件(需口令)")
104
+ p_list.add_argument("payload", type=Path, help="密文包路径(.enc)")
105
+ p_list.add_argument("-p", "--password", default=None, help="口令(默认交互输入)")
106
+ p_list.add_argument(
107
+ "--password-env",
108
+ default=None,
109
+ metavar="VAR",
110
+ help="从环境变量读口令(填变量名;不是口令本身)",
111
+ )
112
+
113
+ return parser
114
+
115
+
116
+ def main(argv: list[str] | None = None) -> int:
117
+ raw = list(sys.argv[1:] if argv is None else argv)
118
+ cli_argv, script_args = _split_script_args(raw)
119
+
120
+ parser = build_parser()
121
+ args = parser.parse_args(cli_argv)
122
+
123
+ if args.command == "encrypt":
124
+ password = _password_from_args(args.password, args.password_env)
125
+ encrypt_main(
126
+ source_dir=str(args.source_dir),
127
+ output_path=str(args.output),
128
+ entry_point=args.entry,
129
+ password=password,
130
+ )
131
+ return 0
132
+
133
+ if args.command == "run":
134
+ password = _password_from_args(args.password, args.password_env)
135
+ entry_name = Path(args.payload).name
136
+ train_argv = [entry_name, *script_args]
137
+ run_main(payload_path=str(args.payload), password=password, argv=train_argv)
138
+ return 0
139
+
140
+ if args.command == "list":
141
+ password = _password_from_args(args.password, args.password_env)
142
+ if password is None:
143
+ import getpass
144
+
145
+ password = getpass.getpass("解密口令: ")
146
+ entry, files = list_payload(args.payload, password)
147
+ print(f"entry: {entry}")
148
+ print(f"files: {len(files)}")
149
+ for name in files:
150
+ print(name)
151
+ return 0
152
+
153
+ parser.error(f"未知命令: {args.command}")
154
+ return 2
155
+
156
+
157
+ if __name__ == "__main__":
158
+ raise SystemExit(main())
@@ -0,0 +1,31 @@
1
+ """Shared payload format constants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ MAGIC = b"PYFE1\0"
6
+ SALT_LEN = 16
7
+ KDF_ITERATIONS = 390_000
8
+ MANIFEST_NAME = "_pyfernet_manifest.json"
9
+
10
+ SKIP_DIRS = {
11
+ ".git",
12
+ "__pycache__",
13
+ ".venv",
14
+ "venv",
15
+ ".mypy_cache",
16
+ ".pytest_cache",
17
+ "node_modules",
18
+ }
19
+ SKIP_SUFFIXES = {".pyc", ".pyo", ".enc"}
20
+ ALLOW_SUFFIXES = {
21
+ ".py",
22
+ ".yaml",
23
+ ".yml",
24
+ ".json",
25
+ ".toml",
26
+ ".cfg",
27
+ ".ini",
28
+ ".txt",
29
+ ".md",
30
+ }
31
+ ALLOW_NAMES = {"LICENSE", "NOTICE"}