Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
450 changes: 221 additions & 229 deletions lib/generator.js

Large diffs are not rendered by default.

235 changes: 230 additions & 5 deletions lib/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ const keywords = [
'lambda', 'nonlocal', 'not', 'or', 'pass',
'raise', 'return', 'try', 'while', 'with', 'yield'
];
// 定义标准库模块列表(Python 3.x常用标准库)
const standardLibraries = new Set([
'__future__', 'abc', 'argparse', 'ast', 'asyncio', 'base64', 'bisect',
'builtins', 'calendar', 'collections', 'concurrent', 'configparser',
'contextlib', 'copy', 'csv', 'datetime', 'decimal', 'difflib',
'email', 'enum', 'errno', 'faulthandler', 'fileinput', 'fnmatch',
'fractions', 'functools', 'gc', 'glob', 'gzip', 'hashlib', 'heapq',
'hmac', 'html', 'http', 'importlib', 'inspect', 'io', 'ipaddress',
'itertools', 'json', 'keyword', 'linecache', 'locale', 'logging',
'lzma', 'math', 'mimetypes', 'multiprocessing', 'numbers', 'operator',
'os', 'pathlib', 'pickle', 'platform', 'pprint', 'queue', 'random',
're', 'reprlib', 'secrets', 'shlex', 'shutil', 'signal', 'socket',
'sqlite3', 'ssl', 'statistics', 'string', 'subprocess', 'sys',
'tarfile', 'tempfile', 'textwrap', 'threading', 'time', 'timeit',
'traceback', 'types', 'typing', 'unittest', 'urllib', 'uuid',
'warnings', 'weakref', 'xml', 'zipfile', 'zlib'
]);

const builtinModels = [
'$Request', '$Response', '$Error', '$SSEEvent', '$Model',
Expand Down Expand Up @@ -192,7 +209,7 @@ function _camelCase(str, split = '_') {
// 如果没有下划线,对整个字符串首字母大写
str = _upperFirst(str);
}

return str;
}

Expand Down Expand Up @@ -258,12 +275,12 @@ function _isBuiltinModel(name) {
}

function _isDefault(expr) {
if(expr.type !== 'call' || expr.left.type !== 'method_call') {
if (expr.type !== 'call' || expr.left.type !== 'method_call') {
return false;
}
const name = _name(expr.left.id);

if(name !== '$default') {
if (name !== '$default') {
return false;
}

Expand Down Expand Up @@ -296,7 +313,7 @@ function _type(name) {
return 'int';
}

if(name === 'float' || name === 'double') {
if (name === 'float' || name === 'double') {
return 'float';
}

Expand Down Expand Up @@ -395,7 +412,7 @@ function _escape(str) {
}

function _removeFilesInDirectory(directoryPath) {
if(!fs.existsSync(directoryPath)) {
if (!fs.existsSync(directoryPath)) {
return;
}
const files = fs.readdirSync(directoryPath);
Expand All @@ -412,6 +429,212 @@ function _removeFilesInDirectory(directoryPath) {
}
}

/**
* 按照Python PEP 8标准对imports进行排序
* @param {Array} imports - import对象数组
* @returns {Array} 排序后的imports数组
*/
function _sortImports(imports) {
function isStandardLibrary(packageName) {
if (!packageName) { return false; }

// 处理子模块,如 os.path -> os
const rootModule = packageName.split('.')[0];
return standardLibraries.has(rootModule);
}

function isThirdPartyLibrary(packageName) {
if (!packageName) { return false; }

// 不是标准库且不是相对导入(不以.开头)
return !isStandardLibrary(packageName) && !packageName.startsWith('.');
}

function isLocalImport(packageName) {
if (!packageName) { return false; }

// 以.开头的相对导入,或者看起来像本地模块的导入
return packageName.startsWith('.') ||
(!isStandardLibrary(packageName) && !isThirdPartyLibrary(packageName));
}

function getImportPriority(importObj) {
const { packageName, className } = importObj;

// 1. __future__ imports (最高优先级)
if (packageName === '__future__') {
return 1;
}

// 2. 标准库 import 语句
if (!packageName && isStandardLibrary(className)) {
return 2;
}

// 3. 标准库 from...import 语句
if (packageName && isStandardLibrary(packageName)) {
return 3;
}

// 4. 第三方库 import 语句
if (!packageName && isThirdPartyLibrary(className)) {
return 4;
}

// 5. 第三方库 from...import 语句
if (packageName && isThirdPartyLibrary(packageName)) {
return 5;
}

// 6. 本地/相对 import 语句
if (!packageName && isLocalImport(className)) {
return 6;
}

// 7. 本地/相对 from...import 语句
if (packageName && isLocalImport(packageName)) {
return 7;
}

// 8. 其他情况
return 8;
}


// 生成排序键
function getSortKey(importObj) {
const { packageName, className } = importObj;
const priority = getImportPriority(importObj);

// 构建排序键:优先级 + 包名 + 类名
const sortPackage = packageName || '';
const sortClass = className || '';

return `${priority.toString().padStart(2, '0')}_${sortPackage}_${sortClass}`;
}

//合并同一包的from imports
function mergeFromImportsByPackage(imports) {
const packageGroups = new Map();
const nonFromImports = [];

for (const importObj of imports) {
if (importObj.packageName) {
if (!packageGroups.has(importObj.packageName)) {
packageGroups.set(importObj.packageName, []);
}
packageGroups.get(importObj.packageName).push(importObj);
} else {
nonFromImports.push(importObj);
}
}

const mergedImports = [...nonFromImports];
for (const [packageName, packageImports] of packageGroups) {
if (packageImports.length === 1) {
mergedImports.push(packageImports[0]);
} else {
const canMerge = packageImports.every(imp => !imp.aliasName);

if (canMerge) {
const classNames = packageImports.map(imp => imp.className).sort();
mergedImports.push({
packageName: packageName,
className: classNames.join(', '),
aliasName: '',
_isMerged: true
});
} else {
mergedImports.push(...packageImports);
}
}
}

return mergedImports;
}


const importMap = new Map();
for (const importObj of imports) {
const key = `${importObj.packageName}|${importObj.className}|${importObj.aliasName}`;

if (importMap.has(key)) {
continue;
}
importMap.set(key, importObj);
}


const mergedImports = mergeFromImportsByPackage(Array.from(importMap.values()));

// 对imports进行排序
const sortedImports = [...mergedImports].sort((a, b) => {
const keyA = getSortKey(a);
const keyB = getSortKey(b);
return keyA.localeCompare(keyB);
});

return sortedImports;
}

// 辅助函数:将排序后的导入转换为Python代码字符串
function _importsToString(sortedImports) {
const lines = [];
let lastPriority = -1;

// 内部函数需要访问外部的getImportPriority
function getImportPriority(importObj) {
const { packageName, className } = importObj;

function isStandardLibrary(packageName) {
if (!packageName) { return false; }
const rootModule = packageName.split('.')[0];
return standardLibraries.has(rootModule);
}

if (packageName === '__future__') { return 1; }
if (!packageName && isStandardLibrary(className)) { return 2; }
if (packageName && isStandardLibrary(packageName)) { return 3; }
if (!packageName && !isStandardLibrary(className)) { return 4; }
if (packageName && !isStandardLibrary(packageName)) { return 5; }
return 6;
}

sortedImports.forEach(importObj => {
const { packageName, className, aliasName } = importObj;
const currentPriority = getImportPriority(importObj);

// 在不同优先级组之间添加空行
if (lastPriority !== -1 && currentPriority !== lastPriority) {
lines.push('');
}

// 生成import语句
let importLine;
if (packageName) {
// from...import 语句
if (aliasName) {
importLine = `from ${packageName} import ${className} as ${aliasName}`;
} else {
importLine = `from ${packageName} import ${className}`;
}
} else {
// import 语句
if (aliasName) {
importLine = `import ${className} as ${aliasName}`;
} else {
importLine = `import ${className}`;
}
}

lines.push(importLine);
lastPriority = currentPriority;
});

return lines.join('\n');
}


module.exports = {
_removeFilesInDirectory,
_name,
Expand All @@ -433,6 +656,8 @@ module.exports = {
_type,
_getImport,
_adaptedQuotes,
_sortImports,
_importsToString,
CORE,
REQUEST,
RESPONSE,
Expand Down
30 changes: 17 additions & 13 deletions tests/expected/alias/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,27 @@
from setuptools import setup, find_packages

"""
setup module for tea_python_tests.
setup module for tea-python-tests.

Created on *

@author: Alibaba
@author: AlibabaCloud SDK Team
"""

PACKAGE = "tea_python_tests"
NAME = "tea_python_tests"
DESCRIPTION = ""
AUTHOR = "Alibaba"
AUTHOR_EMAIL = "sdk-team@alibabacloud.com"
URL = "https://github.com/"
NAME = "tea-python-tests"
DESCRIPTION = "Generate setup.py"
AUTHOR = "AlibabaCloud SDK Team"
AUTHOR_EMAIL = "sdkteam@alibabacloud.com"
URL = "https://github.com/dara"
VERSION = __import__(PACKAGE).__version__
REQUIRES = [
"alibabacloud-tea>=1.0.0",
"source==0.0.1",
"import==1.1.2",
"alibabacloud_tea_util==0.3.11"
"darabonba-core>=1.0.0, <2.0.0",
"source>=0.0.1, <1.0.0",
"import>=1.1.2, <2.0.0",
"alibabacloud_tea_util>=0.3.11, <1.0.0",
"xxx1>=0.0.1, <1.0.0",
"xxx2>=0.0.1, <1.0.0"
]

LONG_DESCRIPTION = ''
Expand All @@ -63,17 +65,19 @@
include_package_data=True,
platforms="any",
install_requires=REQUIRES,
python_requires=">=3.6",
python_requires=">=3.7",
classifiers=(
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development"
)
)
12 changes: 6 additions & 6 deletions tests/expected/alias/tea_python_tests/client.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
from __future__ import annotations
from Import.client import Client as ImportClient
from Source.client import Client as SourceClient
from Alias.alias_client import AliasClient
from alias_source.source_client import SourceClient as AliasSourceSourceClient
from alias_symbol.client import Client as AliasSymbolClient
from alias_symbol import models as alias_symbol_models

from alias_source.source_client import SourceClient as AliasSourceSourceClient
from alias_symbol import models as alias_symbol_models
from alias_symbol.client import Client as AliasSymbolClient
from Alias.alias_client import AliasClient
from Import.client import Client as ImportClient
from Source.client import Client as SourceClient



Expand Down
8 changes: 4 additions & 4 deletions tests/expected/annotation/tea_python_tests/client.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
from __future__ import annotations
from darabonba.core import DaraCore
from darabonba.exceptions import UnretryableException
from darabonba.policy.retry import RetryPolicyContext
from darabonba.request import DaraRequest

from darabonba.core import DaraCore
from darabonba.exceptions import UnretryableException
from darabonba.policy.retry import RetryPolicyContext
from darabonba.request import DaraRequest

"""
* @remarks
Expand Down
2 changes: 0 additions & 2 deletions tests/expected/annotation/tea_python_tests/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
# This file is auto-generated, don't edit it. Thanks.
from __future__ import annotations



from ._test import Test

__all__ = [
Expand Down
Loading