此文件是根据python编译器修改而来,作为记录。可用于后续的自动化操作。
# -*- coding: UTF-8 -*-
# Desc : python项目编译文件,该文件放在编译项目的根目录下,也可配置编译项目路径,需要配置见TODO
# TODO
# 1. 设置python版本
# 2. 需要跳过的文件夹
# 3. 不需要编译的py文件列表
# 4. 项目路径(默认该文件的目录)
import os
import sys
import importlib.util
import py_compile
import struct
__all__ = ["compile_dir", "compile_my_project"]
def _walk_dir(dir, ddir=None, maxlevels=10, quiet=0, exclude_dir=[], exclude_files=[]):
if quiet < 2 and isinstance(dir, os.PathLike):
dir = os.fspath(dir)
if not quiet:
print('Listing {!r}...'.format(dir))
try:
names = os.listdir(dir)
except OSError:
if quiet < 2:
print("Can't list {!r}".format(dir))
names = []
names.sort()
for name in names:
if name == '__pycache__':
continue
# 排除路径
if name in exclude_dir:
continue
fullname = os.path.join(dir, name)
# 排除不需要编译文件
if name in exclude_files:
print("非编译文件:%s " % (name,))
print("复制文件:%s 到 %s " % (name, os.path.join(ddir, name)))
_copy_file(fullname, os.path.join(ddir, name))
continue
# 非py文件
if os.path.isfile(fullname) and not name.endswith(".py"):
print("非py文件:%s " % (name,))
print("复制文件:%s %s " % (fullname, os.path.join(ddir, name)))
os.makedirs(ddir, exist_ok=True)
_copy_file(fullname, os.path.join(ddir, name))
continue
if ddir is not None:
dfile = os.path.join(ddir, name)
else:
dfile = None
if not os.path.isdir(fullname):
yield fullname, ddir
elif (maxlevels > 0 and name != os.curdir and name != os.pardir and
os.path.isdir(fullname) and not os.path.islink(fullname)):
yield from _walk_dir(fullname, ddir=dfile,
maxlevels=maxlevels - 1, quiet=quiet, exclude_dir=exclude_dir,
exclude_files=exclude_files)
def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
quiet=0, legacy=False, optimize=-1,
invalidation_mode=None, exclude_dir=[],
exclude_files=[]):
"""Byte-compile all modules in the given directory tree.
Arguments (only dir is required):
dir: the directory to byte-compile
maxlevels: maximum recursion level (default 10)
ddir: the directory that will be prepended to the path to the
file as it is compiled into each byte-code file.
force: if True, force compilation, even if timestamps are up-to-date
quiet: full output with False or 0, errors only with 1,
no output with 2
legacy: if True, produce legacy pyc paths instead of PEP 3147 paths
optimize: optimization level or -1 for level of the interpreter
workers: maximum number of parallel workers
invalidation_mode: how the up-to-dateness of the pyc will be checked
exclude_dir: 不进行编译的文件目录
exclude_files: 不进行编译的文件
"""
files_and_ddirs = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels,
ddir=ddir, exclude_dir=exclude_dir, exclude_files=exclude_files)
success = True
for file, dfile in files_and_ddirs:
if not compile_file(file, dfile, force, rx, quiet,
legacy, optimize, invalidation_mode):
success = False
return success
def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
legacy=False, optimize=-1,
invalidation_mode=None):
"""Byte-compile one file.
Arguments (only fullname is required):
fullname: the file to byte-compile
ddir: if given, the directory name compiled in to the
byte-code file.
force: if True, force compilation, even if timestamps are up-to-date
quiet: full output with False or 0, errors only with 1,
no output with 2
legacy: if True, produce legacy pyc paths instead of PEP 3147 paths
optimize: optimization level or -1 for level of the interpreter
invalidation_mode: how the up-to-dateness of the pyc will be checked 3.6.10 版本不能要此参数
"""
success = True
if quiet < 2 and isinstance(fullname, os.PathLike):
fullname = os.fspath(fullname)
name = os.path.basename(fullname)
if ddir is not None:
dfile = os.path.join(ddir, name)
else:
dfile = None
if rx is not None:
mo = rx.search(fullname)
if mo:
return success
if os.path.isfile(fullname):
if legacy:
cfile = fullname + 'c'
else:
if optimize >= 0:
opt = optimize if optimize >= 1 else ''
cfile = importlib.util.cache_from_source(
fullname, optimization=opt)
else:
# cfile = importlib.util.cache_from_source(fullname)
cfile = dfile + "c"
head, tail = name[:-3], name[-3:]
if tail == '.py':
if not force:
try:
mtime = int(os.stat(fullname).st_mtime)
expect = struct.pack('<4sll', importlib.util.MAGIC_NUMBER,
0, mtime)
print("dfile====", dfile)
with open(dfile, 'rb') as chandle:
actual = chandle.read(12)
if expect == actual:
print("success---------")
return success
except OSError:
pass
if not quiet:
print('编译文件 {!r}...'.format(fullname))
print("编译目标文件:", cfile)
try:
ok = py_compile.compile(fullname, cfile, dfile, True,
optimize=optimize,
)
except py_compile.PyCompileError as err:
success = False
if quiet >= 2:
return success
elif quiet:
print('*** Error compiling {!r}...'.format(fullname))
else:
print('*** ', end='')
# escape non-printable characters in msg
msg = err.msg.encode(sys.stdout.encoding,
errors='backslashreplace')
msg = msg.decode(sys.stdout.encoding)
print(msg)
except (SyntaxError, UnicodeError, OSError) as e:
success = False
if quiet >= 2:
return success
elif quiet:
print('*** Error compiling {!r}...'.format(fullname))
else:
print('*** ', end='')
print(e.__class__.__name__ + ':', e)
else:
if ok == 0:
success = False
return success
def _copy_file(from_file, to_file):
# 复制文件
os.system("cp %s %s " % (from_file, to_file))
def _check_python_version(version):
# 检校python环境版本
if version == sys.version.split(" ")[0]:
return True
raise Exception(f"python 环境版本不对,期望版本为 {version}")
def compile_my_project(python_version, project_home=None,
exclude_dirs=["migrations", ],
exclude_files=["manage.py", "asgi.py", "wsgi.py", "settings.py"]):
"""
:param python_version: python版本
:param project_home: 项目路径(默认该文件的目录)
:param exclude_dirs: 需要跳过的文件夹
:param exclude_files: 不需要编译的py文件列表
:return:
"""
# 1.检校python环境版本
_check_python_version(python_version)
# 解析项目路径(文件存放的文件夹路径)
if not project_home:
project_home = os.path.dirname(os.path.abspath(__file__))
exclude_files.append(os.path.basename(os.path.abspath(__file__)))
# 项目文件夹名称
project_name = project_home.split(os.sep)[-1]
##=== 编译文件存放路径(放在项目路径的同级目录下,新建 {project_name}_compile/{project_name})
project_compile_home = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
project_name + '_compile', project_name)
print("编译源文件路径:", project_home)
print("编译文件存放路径:", project_compile_home)
os.makedirs(project_compile_home, exist_ok=True)
compile_dir(dir=project_home, ddir=project_compile_home, force=True, exclude_dir=exclude_dirs,
exclude_files=exclude_files)
print("=====编译项目完成。。。")
if __name__ == '__main__':
# 该文件放在编译项目的根目录下
# 编译文件存放路径(放在项目路径的同级目录下,新建 {dir_name}_compile/{dir_name})
# 0. TODO 设置python版本
MY_PROJECT_PYTHON_VERSION = "3.6.10"
# 0. TODO 需要跳过的文件夹
exclude_dir = ["migrations", ]
# 0. TODO 不需要编译的py文件列表
exclude_files = ["manage.py", "asgi.py", "wsgi.py", "settings.py", "settings_prd.py"]
compile_my_project(MY_PROJECT_PYTHON_VERSION, exclude_dirs=exclude_dir, exclude_files=exclude_files)