mcpywrap 0.1.0__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.
- mcpywrap-0.1.0/PKG-INFO +97 -0
- mcpywrap-0.1.0/README.md +75 -0
- mcpywrap-0.1.0/mcpywrap/__init__.py +4 -0
- mcpywrap-0.1.0/mcpywrap/__main__.py +13 -0
- mcpywrap-0.1.0/mcpywrap/builders/AddonsPack.py +226 -0
- mcpywrap-0.1.0/mcpywrap/builders/__init__.py +10 -0
- mcpywrap-0.1.0/mcpywrap/builders/file_handler.py +112 -0
- mcpywrap-0.1.0/mcpywrap/builders/file_merge.py +49 -0
- mcpywrap-0.1.0/mcpywrap/builders/project_builder.py +157 -0
- mcpywrap-0.1.0/mcpywrap/builders/watcher.py +107 -0
- mcpywrap-0.1.0/mcpywrap/cli.py +34 -0
- mcpywrap-0.1.0/mcpywrap/commands/__init__.py +25 -0
- mcpywrap-0.1.0/mcpywrap/commands/add_cmd.py +39 -0
- mcpywrap-0.1.0/mcpywrap/commands/build_cmd.py +58 -0
- mcpywrap-0.1.0/mcpywrap/commands/default_cmd.py +70 -0
- mcpywrap-0.1.0/mcpywrap/commands/dev_cmd.py +111 -0
- mcpywrap-0.1.0/mcpywrap/commands/init_cmd.py +202 -0
- mcpywrap-0.1.0/mcpywrap/commands/mod_cmd.py +24 -0
- mcpywrap-0.1.0/mcpywrap/commands/modsdk_cmd.py +65 -0
- mcpywrap-0.1.0/mcpywrap/commands/publish_cmd.py +45 -0
- mcpywrap-0.1.0/mcpywrap/commands/remove_cmd.py +42 -0
- mcpywrap-0.1.0/mcpywrap/config.py +109 -0
- mcpywrap-0.1.0/mcpywrap/custom_packaging.py +41 -0
- mcpywrap-0.1.0/mcpywrap/minecraft/__init__.py +1 -0
- mcpywrap-0.1.0/mcpywrap/minecraft/addons.py +157 -0
- mcpywrap-0.1.0/mcpywrap/minecraft/config.py +6 -0
- mcpywrap-0.1.0/mcpywrap/minecraft/mod_template.py +52 -0
- mcpywrap-0.1.0/mcpywrap/minecraft/netease_modsdk.py +269 -0
- mcpywrap-0.1.0/mcpywrap/minecraft/template/generate_mod_files.py +114 -0
- mcpywrap-0.1.0/mcpywrap/minecraft/template/mod_template.py +459 -0
- mcpywrap-0.1.0/mcpywrap/utils/__init__.py +6 -0
- mcpywrap-0.1.0/mcpywrap/utils/print_guide.py +21 -0
- mcpywrap-0.1.0/mcpywrap/utils/project_setup.py +112 -0
- mcpywrap-0.1.0/mcpywrap/utils/py3to2_util.py +53 -0
- mcpywrap-0.1.0/mcpywrap/utils/utils.py +34 -0
- mcpywrap-0.1.0/mcpywrap.egg-info/PKG-INFO +97 -0
- mcpywrap-0.1.0/mcpywrap.egg-info/SOURCES.txt +41 -0
- mcpywrap-0.1.0/mcpywrap.egg-info/dependency_links.txt +1 -0
- mcpywrap-0.1.0/mcpywrap.egg-info/entry_points.txt +3 -0
- mcpywrap-0.1.0/mcpywrap.egg-info/requires.txt +8 -0
- mcpywrap-0.1.0/mcpywrap.egg-info/top_level.txt +1 -0
- mcpywrap-0.1.0/pyproject.toml +35 -0
- mcpywrap-0.1.0/setup.cfg +4 -0
mcpywrap-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcpywrap
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A wrapper for Minecraft China Edition Addons management
|
|
5
|
+
Author-email: boybook <boybook@easecation.net>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/EaseCation/mcpywrap
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/EaseCation/mcpywrap/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.6
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: click>=8.0.0
|
|
15
|
+
Requires-Dist: 3to2>=1.1.1
|
|
16
|
+
Requires-Dist: watchdog>=2.0.0
|
|
17
|
+
Requires-Dist: twine>=3.4.0
|
|
18
|
+
Requires-Dist: tomli
|
|
19
|
+
Requires-Dist: tomli-w
|
|
20
|
+
Requires-Dist: setuptools
|
|
21
|
+
Requires-Dist: PyQt5>=5.15.0
|
|
22
|
+
|
|
23
|
+
# 🧰 mcpywrap
|
|
24
|
+
|
|
25
|
+
**《我的世界》中国版 ModSDK 与资源包的全周期管理工具**
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/mcpywrap/)
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
|
|
30
|
+
mcpywrap 是基于 Python 生态的《我的世界》中国版 ModSDK/资源包全周期管理工具,支持依赖管理、语法转换和自动化构建,助力开发者实现高效协作与代码复用。
|
|
31
|
+
|
|
32
|
+
## 🌟 核心特性
|
|
33
|
+
|
|
34
|
+
- 🧩 **模块化开发** - 基于 Addons 维度的依赖管理
|
|
35
|
+
- 🔄 **语法无忧** - 开发阶段支持 Python3 语法,构建时自动转换为 Python2
|
|
36
|
+
- 📦 **生态兼容** - 无缝对接 PyPI 生态,支持标准 `pyproject.toml` 配置
|
|
37
|
+
- 🚀 **智能构建** - 一键打包符合 MCStudio 规范的成品 Addons
|
|
38
|
+
- 🔥 **热重载开发** - 实时监控代码变更,支持 MCStudio 热更新
|
|
39
|
+
|
|
40
|
+
## 📖 为何选择此工具?
|
|
41
|
+
|
|
42
|
+
### 传统开发痛点
|
|
43
|
+
- 📜 **代码复用困难** - 通过 **文件拷贝** 导致版本管理混乱
|
|
44
|
+
- 🚧 **协作效率低下** - 多项目 **重复代码** 维护成本高
|
|
45
|
+
- ⚠️ **语法限制** - 中国版仅支持 Python2,缺乏现代语法支持
|
|
46
|
+
|
|
47
|
+
### mcpywrap 解决方案
|
|
48
|
+
1. **标准化开发**
|
|
49
|
+
通过 `pyproject.toml` 声明项目元数据和依赖关系,实现真正的模块化开发
|
|
50
|
+
|
|
51
|
+
2. **现代开发体验**
|
|
52
|
+
开发阶段使用 Python3 语法,构建时通过 3to2 自动转换,兼顾开发效率与运行兼容
|
|
53
|
+
|
|
54
|
+
3. **生态集成**
|
|
55
|
+
依托 pip 包管理体系,支持依赖的版本锁定和自动解析
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## 🚀 快速开始
|
|
59
|
+
|
|
60
|
+
### 前置要求
|
|
61
|
+
- Python ≥ 3.8
|
|
62
|
+
- pip ≥ 21.0
|
|
63
|
+
|
|
64
|
+
### 安装
|
|
65
|
+
```bash
|
|
66
|
+
pip install mcpywrap
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 初始化项目
|
|
70
|
+
```bash
|
|
71
|
+
mcpy init
|
|
72
|
+
```
|
|
73
|
+
交互式创建项目结构,自动生成标准的 Mod 框架。
|
|
74
|
+
|
|
75
|
+
## 🛠 工作流指南
|
|
76
|
+
|
|
77
|
+
### 依赖管理
|
|
78
|
+
| 命令 | 说明 |
|
|
79
|
+
|-------------------------------|---------------------|
|
|
80
|
+
| `mcpy` | 维护项目,将项目安装到系统 site-package 环境 |
|
|
81
|
+
| `mcpy add <package> [version]` | 添加指定版本依赖 |
|
|
82
|
+
| `mcpy remove <package>` | 移除依赖 |
|
|
83
|
+
|
|
84
|
+
### 构建与开发
|
|
85
|
+
```bash
|
|
86
|
+
# 生产构建(输出到配置的 build 目录)
|
|
87
|
+
mcpy build
|
|
88
|
+
|
|
89
|
+
# 开发模式(热重载 + 实时构建)
|
|
90
|
+
mcpy dev
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 🤝 参与贡献
|
|
94
|
+
欢迎提交 Issue 和 PR!请先阅读 [贡献指南](CONTRIBUTING.md)。
|
|
95
|
+
|
|
96
|
+
## 开源协议
|
|
97
|
+
[MIT License](LICENSE) © 2025 EaseCation
|
mcpywrap-0.1.0/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# 🧰 mcpywrap
|
|
2
|
+
|
|
3
|
+
**《我的世界》中国版 ModSDK 与资源包的全周期管理工具**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/mcpywrap/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
mcpywrap 是基于 Python 生态的《我的世界》中国版 ModSDK/资源包全周期管理工具,支持依赖管理、语法转换和自动化构建,助力开发者实现高效协作与代码复用。
|
|
9
|
+
|
|
10
|
+
## 🌟 核心特性
|
|
11
|
+
|
|
12
|
+
- 🧩 **模块化开发** - 基于 Addons 维度的依赖管理
|
|
13
|
+
- 🔄 **语法无忧** - 开发阶段支持 Python3 语法,构建时自动转换为 Python2
|
|
14
|
+
- 📦 **生态兼容** - 无缝对接 PyPI 生态,支持标准 `pyproject.toml` 配置
|
|
15
|
+
- 🚀 **智能构建** - 一键打包符合 MCStudio 规范的成品 Addons
|
|
16
|
+
- 🔥 **热重载开发** - 实时监控代码变更,支持 MCStudio 热更新
|
|
17
|
+
|
|
18
|
+
## 📖 为何选择此工具?
|
|
19
|
+
|
|
20
|
+
### 传统开发痛点
|
|
21
|
+
- 📜 **代码复用困难** - 通过 **文件拷贝** 导致版本管理混乱
|
|
22
|
+
- 🚧 **协作效率低下** - 多项目 **重复代码** 维护成本高
|
|
23
|
+
- ⚠️ **语法限制** - 中国版仅支持 Python2,缺乏现代语法支持
|
|
24
|
+
|
|
25
|
+
### mcpywrap 解决方案
|
|
26
|
+
1. **标准化开发**
|
|
27
|
+
通过 `pyproject.toml` 声明项目元数据和依赖关系,实现真正的模块化开发
|
|
28
|
+
|
|
29
|
+
2. **现代开发体验**
|
|
30
|
+
开发阶段使用 Python3 语法,构建时通过 3to2 自动转换,兼顾开发效率与运行兼容
|
|
31
|
+
|
|
32
|
+
3. **生态集成**
|
|
33
|
+
依托 pip 包管理体系,支持依赖的版本锁定和自动解析
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## 🚀 快速开始
|
|
37
|
+
|
|
38
|
+
### 前置要求
|
|
39
|
+
- Python ≥ 3.8
|
|
40
|
+
- pip ≥ 21.0
|
|
41
|
+
|
|
42
|
+
### 安装
|
|
43
|
+
```bash
|
|
44
|
+
pip install mcpywrap
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 初始化项目
|
|
48
|
+
```bash
|
|
49
|
+
mcpy init
|
|
50
|
+
```
|
|
51
|
+
交互式创建项目结构,自动生成标准的 Mod 框架。
|
|
52
|
+
|
|
53
|
+
## 🛠 工作流指南
|
|
54
|
+
|
|
55
|
+
### 依赖管理
|
|
56
|
+
| 命令 | 说明 |
|
|
57
|
+
|-------------------------------|---------------------|
|
|
58
|
+
| `mcpy` | 维护项目,将项目安装到系统 site-package 环境 |
|
|
59
|
+
| `mcpy add <package> [version]` | 添加指定版本依赖 |
|
|
60
|
+
| `mcpy remove <package>` | 移除依赖 |
|
|
61
|
+
|
|
62
|
+
### 构建与开发
|
|
63
|
+
```bash
|
|
64
|
+
# 生产构建(输出到配置的 build 目录)
|
|
65
|
+
mcpy build
|
|
66
|
+
|
|
67
|
+
# 开发模式(热重载 + 实时构建)
|
|
68
|
+
mcpy dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 🤝 参与贡献
|
|
72
|
+
欢迎提交 Issue 和 PR!请先阅读 [贡献指南](CONTRIBUTING.md)。
|
|
73
|
+
|
|
74
|
+
## 开源协议
|
|
75
|
+
[MIT License](LICENSE) © 2025 EaseCation
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import click
|
|
6
|
+
from .file_handler import ensure_dir, try_merge_file
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Python 包管理和其他应该忽略的文件和目录
|
|
10
|
+
EXCLUDED_PATTERNS = [
|
|
11
|
+
# Python 包管理
|
|
12
|
+
".egg-info",
|
|
13
|
+
"__pycache__",
|
|
14
|
+
".pyc",
|
|
15
|
+
".pyo",
|
|
16
|
+
".pyd",
|
|
17
|
+
".so",
|
|
18
|
+
".dll",
|
|
19
|
+
".eggs",
|
|
20
|
+
".pytest_cache",
|
|
21
|
+
".tox",
|
|
22
|
+
".coverage",
|
|
23
|
+
".coverage.*",
|
|
24
|
+
"htmlcov",
|
|
25
|
+
# 版本控制
|
|
26
|
+
".git",
|
|
27
|
+
".hg",
|
|
28
|
+
".svn",
|
|
29
|
+
".bzr",
|
|
30
|
+
# 其他临时文件
|
|
31
|
+
".DS_Store",
|
|
32
|
+
"Thumbs.db"
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
MANIFEST_FILES = [
|
|
36
|
+
"manifest.json",
|
|
37
|
+
"pack_manifest.json"
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AddonsPack(object):
|
|
42
|
+
|
|
43
|
+
pkg_name: str
|
|
44
|
+
path: str
|
|
45
|
+
is_origin: bool
|
|
46
|
+
behavior_pack_dir: str
|
|
47
|
+
resource_pack_dir: str
|
|
48
|
+
|
|
49
|
+
def __init__(self, pkg_name, path, is_origin=False):
|
|
50
|
+
self.pkg_name = pkg_name
|
|
51
|
+
self.path = path
|
|
52
|
+
self.is_origin = is_origin
|
|
53
|
+
self.behavior_pack_dir = None
|
|
54
|
+
self.resource_pack_dir = None
|
|
55
|
+
# 进入此目录,查找内部的行为包和资源包的路径
|
|
56
|
+
os.chdir(self.path)
|
|
57
|
+
for item in os.listdir(self.path):
|
|
58
|
+
item_path = os.path.join(self.path, item)
|
|
59
|
+
if os.path.isdir(item_path):
|
|
60
|
+
if item.startswith("behavior_pack") or item.startswith("BehaviorPack"):
|
|
61
|
+
self.behavior_pack_dir = item_path
|
|
62
|
+
elif item.startswith("resource_pack") or item.startswith("ResourcePack"):
|
|
63
|
+
self.resource_pack_dir = item_path
|
|
64
|
+
if not self.behavior_pack_dir:
|
|
65
|
+
self.behavior_pack_dir = os.path.join(self.path, "behavior_pack")
|
|
66
|
+
if not self.resource_pack_dir:
|
|
67
|
+
self.resource_pack_dir = os.path.join(self.path, "resource_pack")
|
|
68
|
+
|
|
69
|
+
def should_exclude(self, path):
|
|
70
|
+
"""判断文件或目录是否应该被排除"""
|
|
71
|
+
for pattern in EXCLUDED_PATTERNS:
|
|
72
|
+
if pattern in path:
|
|
73
|
+
return True
|
|
74
|
+
# 得到文件名
|
|
75
|
+
filename = os.path.basename(path)
|
|
76
|
+
if not self.is_origin and filename in MANIFEST_FILES:
|
|
77
|
+
return True
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
def copy_behavior_to(self, target_dir: str):
|
|
81
|
+
"""复制行为包和资源包到目标目录"""
|
|
82
|
+
if self.behavior_pack_dir:
|
|
83
|
+
target_path = os.path.join(target_dir, os.path.basename(self.behavior_pack_dir))
|
|
84
|
+
os.makedirs(target_path, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
# 使用自定义复制函数而不是shutil.copytree
|
|
87
|
+
for root, dirs, files in os.walk(self.behavior_pack_dir):
|
|
88
|
+
# 过滤掉应该排除的目录
|
|
89
|
+
dirs[:] = [d for d in dirs if not self.should_exclude(os.path.join(root, d))]
|
|
90
|
+
|
|
91
|
+
# 计算相对路径
|
|
92
|
+
rel_path = os.path.relpath(root, self.behavior_pack_dir)
|
|
93
|
+
# 计算目标目录
|
|
94
|
+
target_root = os.path.join(target_path, rel_path) if rel_path != '.' else target_path
|
|
95
|
+
ensure_dir(target_root)
|
|
96
|
+
|
|
97
|
+
# 复制文件
|
|
98
|
+
for file in files:
|
|
99
|
+
src_file = os.path.join(root, file)
|
|
100
|
+
if not self.should_exclude(src_file):
|
|
101
|
+
dest_file = os.path.join(target_root, file)
|
|
102
|
+
# 如果是Python文件,检查并添加编码声明
|
|
103
|
+
if file.endswith('.py'):
|
|
104
|
+
self._copy_with_encoding_check(src_file, dest_file)
|
|
105
|
+
else:
|
|
106
|
+
shutil.copy2(src_file, dest_file)
|
|
107
|
+
|
|
108
|
+
def copy_resource_to(self, target_dir: str):
|
|
109
|
+
"""复制资源包到目标目录"""
|
|
110
|
+
if self.resource_pack_dir:
|
|
111
|
+
target_path = os.path.join(target_dir, os.path.basename(self.resource_pack_dir))
|
|
112
|
+
os.makedirs(target_path, exist_ok=True)
|
|
113
|
+
|
|
114
|
+
# 使用自定义复制函数而不是shutil.copytree
|
|
115
|
+
for root, dirs, files in os.walk(self.resource_pack_dir):
|
|
116
|
+
# 过滤掉应该排除的目录
|
|
117
|
+
dirs[:] = [d for d in dirs if not self.should_exclude(os.path.join(root, d))]
|
|
118
|
+
|
|
119
|
+
# 计算相对路径
|
|
120
|
+
rel_path = os.path.relpath(root, self.resource_pack_dir)
|
|
121
|
+
# 计算目标目录
|
|
122
|
+
target_root = os.path.join(target_path, rel_path) if rel_path != '.' else target_path
|
|
123
|
+
ensure_dir(target_root)
|
|
124
|
+
|
|
125
|
+
# 复制文件
|
|
126
|
+
for file in files:
|
|
127
|
+
src_file = os.path.join(root, file)
|
|
128
|
+
if not self.should_exclude(src_file):
|
|
129
|
+
dest_file = os.path.join(target_root, file)
|
|
130
|
+
# 如果是Python文件,检查并添加编码声明
|
|
131
|
+
if file.endswith('.py'):
|
|
132
|
+
self._copy_with_encoding_check(src_file, dest_file)
|
|
133
|
+
else:
|
|
134
|
+
shutil.copy2(src_file, dest_file)
|
|
135
|
+
|
|
136
|
+
def merge_behavior_into(self, target_behavior_dir: str):
|
|
137
|
+
"""合并行为包到目标行为包目录"""
|
|
138
|
+
if self.behavior_pack_dir:
|
|
139
|
+
for root, dirs, files in os.walk(self.behavior_pack_dir):
|
|
140
|
+
# 过滤掉应该排除的目录
|
|
141
|
+
dirs[:] = [d for d in dirs if not self.should_exclude(os.path.join(root, d))]
|
|
142
|
+
|
|
143
|
+
# 计算相对路径
|
|
144
|
+
rel_path = os.path.relpath(root, self.behavior_pack_dir)
|
|
145
|
+
# 计算目标目录
|
|
146
|
+
target_root = os.path.join(target_behavior_dir, rel_path) if rel_path != '.' else target_behavior_dir
|
|
147
|
+
ensure_dir(target_root)
|
|
148
|
+
|
|
149
|
+
# 复制文件
|
|
150
|
+
for file in files:
|
|
151
|
+
src_file = os.path.join(root, file)
|
|
152
|
+
if self.should_exclude(src_file):
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
dest_file = os.path.join(target_root, file)
|
|
156
|
+
|
|
157
|
+
if file.endswith('.py'): # 如果是Python文件,直接无脑覆盖
|
|
158
|
+
self._copy_with_encoding_check(src_file, dest_file)
|
|
159
|
+
elif os.path.exists(dest_file): # 如果文件已存在,尝试合并
|
|
160
|
+
suc, reason = try_merge_file(src_file, dest_file)
|
|
161
|
+
if not suc:
|
|
162
|
+
click.secho(f"❌ 未处理的文件冲突: {src_file} -> {dest_file} {reason}", fg="red")
|
|
163
|
+
else:
|
|
164
|
+
shutil.copy2(src_file, dest_file)
|
|
165
|
+
|
|
166
|
+
def merge_resource_into(self, target_resource_dir: str):
|
|
167
|
+
"""合并资源包到目标资源包目录"""
|
|
168
|
+
if self.resource_pack_dir:
|
|
169
|
+
for root, dirs, files in os.walk(self.resource_pack_dir):
|
|
170
|
+
# 过滤掉应该排除的目录
|
|
171
|
+
dirs[:] = [d for d in dirs if not self.should_exclude(os.path.join(root, d))]
|
|
172
|
+
|
|
173
|
+
# 计算相对路径
|
|
174
|
+
rel_path = os.path.relpath(root, self.resource_pack_dir)
|
|
175
|
+
# 计算目标目录
|
|
176
|
+
target_root = os.path.join(target_resource_dir, rel_path) if rel_path != '.' else target_resource_dir
|
|
177
|
+
ensure_dir(target_root)
|
|
178
|
+
|
|
179
|
+
# 复制文件
|
|
180
|
+
for file in files:
|
|
181
|
+
src_file = os.path.join(root, file)
|
|
182
|
+
if self.should_exclude(src_file):
|
|
183
|
+
continue
|
|
184
|
+
|
|
185
|
+
dest_file = os.path.join(target_root, file)
|
|
186
|
+
# 处理文件冲突
|
|
187
|
+
if file.endswith('.py'):
|
|
188
|
+
self._copy_with_encoding_check(src_file, dest_file)
|
|
189
|
+
elif os.path.exists(dest_file):
|
|
190
|
+
suc, reason = try_merge_file(src_file, dest_file)
|
|
191
|
+
if not suc:
|
|
192
|
+
click.secho(f"⚠️ 警告: 文件合并异常 {src_file} -> {dest_file} {reason}", fg="yellow")
|
|
193
|
+
# 如果是Python文件,检查并添加编码声明
|
|
194
|
+
if file.endswith('.py'):
|
|
195
|
+
self._copy_with_encoding_check(src_file, dest_file)
|
|
196
|
+
else:
|
|
197
|
+
shutil.copy2(src_file, dest_file)
|
|
198
|
+
else:
|
|
199
|
+
shutil.copy2(src_file, dest_file)
|
|
200
|
+
|
|
201
|
+
def _copy_with_encoding_check(self, src_file, dest_file):
|
|
202
|
+
"""复制Python文件,并检查添加编码声明"""
|
|
203
|
+
try:
|
|
204
|
+
with open(src_file, 'r', encoding='utf-8') as f:
|
|
205
|
+
content = f.read()
|
|
206
|
+
|
|
207
|
+
# 检查是否有编码声明
|
|
208
|
+
has_coding = False
|
|
209
|
+
first_line = content.splitlines()[0] if content.splitlines() else ""
|
|
210
|
+
if "# -*- coding: utf-8 -*-" in first_line or "# coding: utf-8" in first_line:
|
|
211
|
+
has_coding = True
|
|
212
|
+
|
|
213
|
+
# 如果没有编码声明,则添加
|
|
214
|
+
if not has_coding:
|
|
215
|
+
content = "# -*- coding: utf-8 -*-\n" + content
|
|
216
|
+
|
|
217
|
+
# 写入目标文件
|
|
218
|
+
with open(dest_file, 'w', encoding='utf-8') as f:
|
|
219
|
+
f.write(content)
|
|
220
|
+
|
|
221
|
+
# 复制文件元数据
|
|
222
|
+
shutil.copystat(src_file, dest_file)
|
|
223
|
+
except Exception as e:
|
|
224
|
+
click.secho(f"⚠️ 添加编码声明时出错: {src_file} -> {dest_file}: {str(e)}", fg="yellow")
|
|
225
|
+
# 如果出错,则直接复制
|
|
226
|
+
shutil.copy2(src_file, dest_file)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
构建模块包,提供项目构建、文件处理和监控功能
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .project_builder import build_project
|
|
7
|
+
from .file_handler import process_file, is_python_file
|
|
8
|
+
from .watcher import FileWatcher
|
|
9
|
+
|
|
10
|
+
__all__ = ['build_project', 'process_file', 'is_python_file', 'FileWatcher']
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
文件处理模块 - 负责单个文件的复制和转换
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
|
|
9
|
+
from ..utils.py3to2_util import py3_to_2
|
|
10
|
+
from ..utils.utils import ensure_dir, run_command
|
|
11
|
+
from .file_merge import try_merge_file
|
|
12
|
+
|
|
13
|
+
def is_python_file(file_path):
|
|
14
|
+
"""判断是否为Python文件"""
|
|
15
|
+
return file_path.endswith('.py')
|
|
16
|
+
|
|
17
|
+
def copy_file(src_path, dest_path):
|
|
18
|
+
"""复制文件,确保目标目录存在"""
|
|
19
|
+
dest_dir = os.path.dirname(dest_path)
|
|
20
|
+
ensure_dir(dest_dir)
|
|
21
|
+
print(f"复制文件: {src_path} 到 {dest_path}")
|
|
22
|
+
shutil.copy2(src_path, dest_path)
|
|
23
|
+
return dest_path
|
|
24
|
+
|
|
25
|
+
def convert_py3_to_py2(file_path):
|
|
26
|
+
"""将某个Python文件转换为Python 2"""
|
|
27
|
+
try:
|
|
28
|
+
# 首先尝试使用直接的Python API调用
|
|
29
|
+
from lib3to2.main import main
|
|
30
|
+
# main函数接受包名和参数列表
|
|
31
|
+
# 第一个参数是包名 'lib3to2' (这是3to2所有修复器的位置)
|
|
32
|
+
# 第二个参数是命令行参数列表
|
|
33
|
+
exit_code = main('lib3to2.fixes', ['-w', '-n', '--no-diffs', file_path, '--nofix=metaclass'])
|
|
34
|
+
# exit_code = py3_to_2(file_path)
|
|
35
|
+
return exit_code == 0, "转换完成" if exit_code == 0 else f"转换失败,错误代码: {exit_code}"
|
|
36
|
+
except Exception as e:
|
|
37
|
+
# 如果直接调用失败,则尝试命令行方式(作为备选)
|
|
38
|
+
try:
|
|
39
|
+
# 方法1:直接命令行调用
|
|
40
|
+
success, output = run_command(["3to2", "-w", "-n", file_path])
|
|
41
|
+
if not success:
|
|
42
|
+
# 方法2:使用shell=True参数
|
|
43
|
+
success, output = run_command(["3to2", "-w", "-n", file_path], shell=True)
|
|
44
|
+
|
|
45
|
+
return success, output
|
|
46
|
+
except Exception as cmd_e:
|
|
47
|
+
return False, f"Python API调用失败: {str(e)}\n命令行调用也失败: {str(cmd_e)}"
|
|
48
|
+
|
|
49
|
+
def process_file(src_path, source_dir, target_dir, is_dependency=False, dependency_name=None):
|
|
50
|
+
"""处理单个文件(复制并根据文件类型处理)"""
|
|
51
|
+
# 计算相对路径和目标路径
|
|
52
|
+
rel_path = os.path.relpath(src_path, source_dir)
|
|
53
|
+
|
|
54
|
+
# 如果是依赖项,需要特殊处理目标路径
|
|
55
|
+
if is_dependency:
|
|
56
|
+
# 确定是属于行为包还是资源包
|
|
57
|
+
if "behavior_pack" in src_path.lower() or "behaviorpack" in src_path.lower():
|
|
58
|
+
# 在路径中定位behavior_pack部分
|
|
59
|
+
parts = src_path.split(os.sep)
|
|
60
|
+
for i, part in enumerate(parts):
|
|
61
|
+
if "behavior_pack" in part.lower() or "behaviorpack" in part.lower():
|
|
62
|
+
# 获取behavior_pack后的相对路径
|
|
63
|
+
sub_path = os.path.join(*parts[i+1:])
|
|
64
|
+
# 目标路径应该是behavior_pack目录
|
|
65
|
+
for item in os.listdir(target_dir):
|
|
66
|
+
if "behavior_pack" in item.lower() or "behaviorpack" in item.lower():
|
|
67
|
+
dest_path = os.path.join(target_dir, item, sub_path)
|
|
68
|
+
break
|
|
69
|
+
else:
|
|
70
|
+
# 如果找不到behavior_pack目录,创建一个
|
|
71
|
+
dest_path = os.path.join(target_dir, "behavior_pack", sub_path)
|
|
72
|
+
break
|
|
73
|
+
elif "resource_pack" in src_path.lower() or "resourcepack" in src_path.lower():
|
|
74
|
+
# 在路径中定位resource_pack部分
|
|
75
|
+
parts = src_path.split(os.sep)
|
|
76
|
+
for i, part in enumerate(parts):
|
|
77
|
+
if "resource_pack" in part.lower() or "resourcepack" in part.lower():
|
|
78
|
+
# 获取resource_pack后的相对路径
|
|
79
|
+
sub_path = os.path.join(*parts[i+1:])
|
|
80
|
+
# 目标路径应该是resource_pack目录
|
|
81
|
+
for item in os.listdir(target_dir):
|
|
82
|
+
if "resource_pack" in item.lower() or "resourcepack" in item.lower():
|
|
83
|
+
dest_path = os.path.join(target_dir, item, sub_path)
|
|
84
|
+
break
|
|
85
|
+
else:
|
|
86
|
+
# 如果找不到resource_pack目录,创建一个
|
|
87
|
+
dest_path = os.path.join(target_dir, "resource_pack", sub_path)
|
|
88
|
+
break
|
|
89
|
+
else:
|
|
90
|
+
# 如果不在行为包或资源包中,忽略该文件
|
|
91
|
+
return False, "不在行为包或资源包中的文件将被忽略", None
|
|
92
|
+
else:
|
|
93
|
+
dest_path = os.path.join(target_dir, rel_path)
|
|
94
|
+
|
|
95
|
+
# 检查目标文件是否已存在,如果存在且来自依赖,尝试合并
|
|
96
|
+
if is_dependency and os.path.exists(dest_path):
|
|
97
|
+
suc, reason = try_merge_file(src_path, dest_path)
|
|
98
|
+
if not suc:
|
|
99
|
+
# 如果合并失败或有冲突,直接返回
|
|
100
|
+
msg = f"文件合并失败或存在冲突" if reason is None else reason
|
|
101
|
+
return False, msg, dest_path
|
|
102
|
+
else:
|
|
103
|
+
# 复制文件
|
|
104
|
+
copy_file(src_path, dest_path)
|
|
105
|
+
|
|
106
|
+
# 如果是Python文件,进行转换
|
|
107
|
+
if is_python_file(src_path):
|
|
108
|
+
success, output = convert_py3_to_py2(dest_path)
|
|
109
|
+
return success, output, dest_path
|
|
110
|
+
|
|
111
|
+
# 如果是其他类型文件,直接返回成功
|
|
112
|
+
return True, "", dest_path
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def try_merge_file(source_file, target_file) -> tuple[bool, str]:
|
|
7
|
+
"""合并两个JSON文件的内容"""
|
|
8
|
+
# 如果是py文件,直接复制即可
|
|
9
|
+
if source_file.endswith('.py'):
|
|
10
|
+
# 直接复制
|
|
11
|
+
shutil.copy2(source_file, target_file)
|
|
12
|
+
return True, f"成功复制 {os.path.basename(source_file)}"
|
|
13
|
+
else:
|
|
14
|
+
# 读取源文件内容
|
|
15
|
+
with open(source_file, 'r', encoding='utf-8') as f:
|
|
16
|
+
source_content = f.read()
|
|
17
|
+
|
|
18
|
+
# 读取目标文件内容
|
|
19
|
+
with open(target_file, 'r', encoding='utf-8') as f:
|
|
20
|
+
target_content = f.read()
|
|
21
|
+
|
|
22
|
+
# 合并两个JSON对象
|
|
23
|
+
try:
|
|
24
|
+
source_json = eval(source_content)
|
|
25
|
+
target_json = eval(target_content)
|
|
26
|
+
merged_json = _merge_dicts(target_json, source_json)
|
|
27
|
+
|
|
28
|
+
# 写入合并后的内容到目标文件
|
|
29
|
+
with open(target_file, 'w', encoding='utf-8') as f:
|
|
30
|
+
f.write(str(merged_json))
|
|
31
|
+
|
|
32
|
+
return True, f"成功合并 {os.path.basename(source_file)} 到 {os.path.basename(target_file)}"
|
|
33
|
+
except Exception as e:
|
|
34
|
+
return False, f"合并失败: {str(e)}"
|
|
35
|
+
|
|
36
|
+
def _merge_dicts(dict1, dict2):
|
|
37
|
+
"""递归合并两个字典"""
|
|
38
|
+
result = dict1.copy()
|
|
39
|
+
for key, value in dict2.items():
|
|
40
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
41
|
+
# 递归合并嵌套字典
|
|
42
|
+
result[key] = _merge_dicts(result[key], value)
|
|
43
|
+
elif key in result and isinstance(result[key], list) and isinstance(value, list):
|
|
44
|
+
# 合并列表
|
|
45
|
+
result[key].extend(value)
|
|
46
|
+
else:
|
|
47
|
+
# 覆盖或添加新键
|
|
48
|
+
result[key] = value
|
|
49
|
+
return result
|