pyobject 1.2.9__tar.gz → 1.2.9.2__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.
- {pyobject-1.2.9/pyobject.egg-info → pyobject-1.2.9.2}/PKG-INFO +7 -7
- {pyobject-1.2.9 → pyobject-1.2.9.2}/README.rst +6 -6
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/__init__.py +4 -4
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/objproxy/__init__.py +78 -43
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/objproxy/builtin_hook.py +56 -6
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/objproxy/dynobj.py +4 -4
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/objproxy/optimize.py +6 -5
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/objproxy/utils.py +1 -1
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/pyobj_extension.c +1 -1
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/tests/test_objproxy.py +1 -1
- pyobject-1.2.9.2/pyobject/tests/test_objproxy_perf.py +49 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2/pyobject.egg-info}/PKG-INFO +7 -7
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject.egg-info/SOURCES.txt +2 -1
- {pyobject-1.2.9 → pyobject-1.2.9.2}/LICENSE +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/MANIFEST.in +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/browser.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/code.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/examples/README.md +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/examples/__init__.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/examples/empty_cp37.pyc +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/examples/pyc_zipper_unpack.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/examples/pyc_zipper_zlib.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/examples/test_inject.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/back.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/codeobject.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/dict.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/empty_dict.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/empty_list.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/empty_tuple.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/forward.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/function.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/list.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/number.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/python.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/python.ico +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/refresh.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/string.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/images/tuple.gif +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/other/browser_chs_locale.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/search.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject/tests/test_code.py +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject.egg-info/dependency_links.txt +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject.egg-info/requires.txt +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/pyobject.egg-info/top_level.txt +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/setup.cfg +0 -0
- {pyobject-1.2.9 → pyobject-1.2.9.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyobject
|
|
3
|
-
Version: 1.2.9
|
|
3
|
+
Version: 1.2.9.2
|
|
4
4
|
Summary: A multifunctional all-in-one utility tool for managing internal Python objects, compatible with nearly all Python 3 versions. 一个多功能合一的提供操作Python对象底层工具的Python包, 支持几乎所有Python 3版本。
|
|
5
5
|
Home-page: https://github.com/qfcy/pyobject
|
|
6
6
|
Author: qfcy
|
|
@@ -351,7 +351,7 @@ Python内部的字节码对象\ ``CodeType``\ ,如\ ``func.__code__``\ ,是
|
|
|
351
351
|
unused_var = func(temp_var)
|
|
352
352
|
|
|
353
353
|
| 代码中\ ``temp_var``\ 只有一条指向\ ``unused_var``\ 的边,而\ ``unused_var``\ 没有任何指出的边。
|
|
354
|
-
| 将\ ``temp_var``\ 的值代入\ ``func(temp_var)``\
|
|
354
|
+
| 将\ ``temp_var``\ 的值代入\ ``func(temp_var)``\ 进行内联,得到\ ``unused_var = func([1,2,3])``\ ,再去掉\ ``unused_var``\ ,优化后的代码会变成\ ``func([1,2,3])``\ 。
|
|
355
355
|
|
|
356
356
|
模块\ ``pyobject.pyobj_extension``
|
|
357
357
|
----------------------------------
|
|
@@ -394,7 +394,7 @@ Python内部的字节码对象\ ``CodeType``\ ,如\ ``func.__code__``\ ,是
|
|
|
394
394
|
lst调用多次==运算符(``__eq__``)相比,
|
|
395
395
|
本函数直接比较对象的指针,提高了效率。
|
|
396
396
|
|
|
397
|
-
**``pyobject``\ 当前版本**: 1.2.9
|
|
397
|
+
**``pyobject``\ 当前版本**: 1.2.9.2
|
|
398
398
|
|
|
399
399
|
更新日志:
|
|
400
400
|
---------
|
|
@@ -829,9 +829,9 @@ Implementation Details
|
|
|
829
829
|
|
|
830
830
|
| Here, ``temp_var`` only has one edge pointing to ``unused_var``, while
|
|
831
831
|
``unused_var`` does not point to any other node.
|
|
832
|
-
| By inlining the value of ``temp_var`` into ``func(temp_var)
|
|
833
|
-
|
|
834
|
-
``func([1, 2, 3])``.
|
|
832
|
+
| By inlining the value of ``temp_var`` into ``func(temp_var)``, the
|
|
833
|
+
code becomes ``unused_var = func([1,2,3])``. After removing
|
|
834
|
+
``unused_var``, the optimized code is ``func([1, 2, 3])``.
|
|
835
835
|
|
|
836
836
|
Module: ``pyobj_extension``
|
|
837
837
|
---------------------------
|
|
@@ -886,7 +886,7 @@ Python call “obj in lst” that invokes the “==” operator (``__eq__``)
|
|
|
886
886
|
multiple times, this function directly compares the pointers to improve
|
|
887
887
|
efficiency.
|
|
888
888
|
|
|
889
|
-
**Current Version of ``pyobject``**: 1.2.9
|
|
889
|
+
**Current Version of ``pyobject``**: 1.2.9.2
|
|
890
890
|
|
|
891
891
|
Change Log
|
|
892
892
|
----------
|
|
@@ -334,7 +334,7 @@ Python内部的字节码对象\ ``CodeType``\ ,如\ ``func.__code__``\ ,是
|
|
|
334
334
|
unused_var = func(temp_var)
|
|
335
335
|
|
|
336
336
|
| 代码中\ ``temp_var``\ 只有一条指向\ ``unused_var``\ 的边,而\ ``unused_var``\ 没有任何指出的边。
|
|
337
|
-
| 将\ ``temp_var``\ 的值代入\ ``func(temp_var)``\
|
|
337
|
+
| 将\ ``temp_var``\ 的值代入\ ``func(temp_var)``\ 进行内联,得到\ ``unused_var = func([1,2,3])``\ ,再去掉\ ``unused_var``\ ,优化后的代码会变成\ ``func([1,2,3])``\ 。
|
|
338
338
|
|
|
339
339
|
模块\ ``pyobject.pyobj_extension``
|
|
340
340
|
----------------------------------
|
|
@@ -377,7 +377,7 @@ Python内部的字节码对象\ ``CodeType``\ ,如\ ``func.__code__``\ ,是
|
|
|
377
377
|
lst调用多次==运算符(``__eq__``)相比,
|
|
378
378
|
本函数直接比较对象的指针,提高了效率。
|
|
379
379
|
|
|
380
|
-
**``pyobject``\ 当前版本**: 1.2.9
|
|
380
|
+
**``pyobject``\ 当前版本**: 1.2.9.2
|
|
381
381
|
|
|
382
382
|
更新日志:
|
|
383
383
|
---------
|
|
@@ -812,9 +812,9 @@ Implementation Details
|
|
|
812
812
|
|
|
813
813
|
| Here, ``temp_var`` only has one edge pointing to ``unused_var``, while
|
|
814
814
|
``unused_var`` does not point to any other node.
|
|
815
|
-
| By inlining the value of ``temp_var`` into ``func(temp_var)
|
|
816
|
-
|
|
817
|
-
``func([1, 2, 3])``.
|
|
815
|
+
| By inlining the value of ``temp_var`` into ``func(temp_var)``, the
|
|
816
|
+
code becomes ``unused_var = func([1,2,3])``. After removing
|
|
817
|
+
``unused_var``, the optimized code is ``func([1, 2, 3])``.
|
|
818
818
|
|
|
819
819
|
Module: ``pyobj_extension``
|
|
820
820
|
---------------------------
|
|
@@ -869,7 +869,7 @@ Python call “obj in lst” that invokes the “==” operator (``__eq__``)
|
|
|
869
869
|
multiple times, this function directly compares the pointers to improve
|
|
870
870
|
efficiency.
|
|
871
871
|
|
|
872
|
-
**Current Version of ``pyobject``**: 1.2.9
|
|
872
|
+
**Current Version of ``pyobject``**: 1.2.9.2
|
|
873
873
|
|
|
874
874
|
Change Log
|
|
875
875
|
----------
|
|
@@ -6,7 +6,7 @@ import sys
|
|
|
6
6
|
from warnings import warn
|
|
7
7
|
from pprint import pprint
|
|
8
8
|
|
|
9
|
-
__version__="1.2.9"
|
|
9
|
+
__version__="1.2.9.2"
|
|
10
10
|
|
|
11
11
|
__all__=["objectname","bases","describe","desc"]
|
|
12
12
|
_ignore_names=["__builtins__","__doc__"]
|
|
@@ -97,10 +97,10 @@ try:
|
|
|
97
97
|
"set_type_subclasses_by_cls"])
|
|
98
98
|
except ImportError:warn("Failed to import pyobject.pyobj_extension.")
|
|
99
99
|
try:
|
|
100
|
-
from pyobject.objproxy import DynObj,ObjChain,ProxiedObj
|
|
101
|
-
__all__.extend(["DynObj","ObjChain","ProxiedObj"
|
|
100
|
+
from pyobject.objproxy import DynObj,ObjChain,ProxiedObj
|
|
101
|
+
__all__.extend(["DynObj","ObjChain","ProxiedObj"])
|
|
102
102
|
except (ImportError, SyntaxError):
|
|
103
|
-
warn("Failed to import pyobject.objproxy.") #
|
|
103
|
+
warn("Failed to import pyobject.objproxy.") # Python 3.5及以下不支持f-string,无法使用objproxy库
|
|
104
104
|
|
|
105
105
|
def desc_demo():
|
|
106
106
|
try:
|
|
@@ -19,28 +19,32 @@ INDENT = 4
|
|
|
19
19
|
NOCODE_EXPORT_ATTRS = ["__class__","__dict__"] # 不留下代码记录的导出属性
|
|
20
20
|
DEFAULT_EXPORT_FUNCS = [] # 在magic_meth_chained中自动生成
|
|
21
21
|
|
|
22
|
-
def unproxy_obj(obj):
|
|
23
|
-
|
|
22
|
+
def unproxy_obj(obj):
|
|
23
|
+
if _isinstance(obj, ProxiedObj):
|
|
24
24
|
obj = obj._ProxiedObj__target_obj
|
|
25
|
+
return obj
|
|
25
26
|
|
|
27
|
+
def unproxy_obj_recursive(obj): # 递归解除obj的代理(备用函数)
|
|
28
|
+
while _isinstance(obj, ProxiedObj):
|
|
29
|
+
obj = obj._ProxiedObj__target_obj
|
|
26
30
|
# 递归处理容器
|
|
27
|
-
if
|
|
28
|
-
return {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
if _isinstance(obj, dict):
|
|
32
|
+
return {unproxy_obj_recursive(k): unproxy_obj_recursive(v)
|
|
33
|
+
for k, v in obj.items()}
|
|
34
|
+
elif _isinstance(obj, list):
|
|
35
|
+
return [unproxy_obj_recursive(item) for item in obj]
|
|
36
|
+
elif _isinstance(obj, tuple):
|
|
37
|
+
return tuple(unproxy_obj_recursive(item) for item in obj)
|
|
38
|
+
elif _isinstance(obj, set):
|
|
39
|
+
return {unproxy_obj_recursive(item) for item in obj}
|
|
35
40
|
else:
|
|
36
41
|
return obj
|
|
37
|
-
|
|
38
42
|
def accept_raw_obj(func,process_arg=None,process_kw=None,process_ret=None):
|
|
39
43
|
# 装饰器,用于有target_obj时只接受原始对象而不是ProxiedObj的函数(备用)
|
|
40
44
|
@functools.wraps(func)
|
|
41
45
|
def inner(*args,**kw):
|
|
42
|
-
args =
|
|
43
|
-
kw =
|
|
46
|
+
args = unproxy_obj_recursive(args)
|
|
47
|
+
kw = unproxy_obj_recursive(kw)
|
|
44
48
|
|
|
45
49
|
if process_arg is not None:
|
|
46
50
|
args = process_arg(args)
|
|
@@ -56,9 +60,9 @@ def accept_raw_obj(func,process_arg=None,process_kw=None,process_ret=None):
|
|
|
56
60
|
return result
|
|
57
61
|
return inner
|
|
58
62
|
|
|
59
|
-
class
|
|
63
|
+
class _EmptyTarget:
|
|
60
64
|
def __bool__(self):return False
|
|
61
|
-
EMPTY_OBJ =
|
|
65
|
+
EMPTY_OBJ = _EmptyTarget() # 空对象的特殊值
|
|
62
66
|
class ReprFormatProxy:
|
|
63
67
|
def __init__(self,target_obj,repr_func):
|
|
64
68
|
self.target_obj = target_obj
|
|
@@ -77,7 +81,7 @@ def basic_repr(obj):
|
|
|
77
81
|
return object.__repr__(obj)
|
|
78
82
|
|
|
79
83
|
def is_trivial_obj(obj): # 检查对象能否被repr()表示(即对象必须是基本类型)
|
|
80
|
-
if
|
|
84
|
+
if _isinstance(obj, ProxiedObj):
|
|
81
85
|
obj = obj._ProxiedObj__target_obj
|
|
82
86
|
if obj is EMPTY_OBJ:return True
|
|
83
87
|
if type(obj) not in TRIVIAL_TYPES: # 不使用isinstance(由于不能是基本类型子类)
|
|
@@ -93,21 +97,39 @@ def is_builtin_type(cls): # 备用函数
|
|
|
93
97
|
flag = get_type_flag(cls)
|
|
94
98
|
if not flag & Py_TPFLAGS_HEAPTYPE or flag & _Py_TPFLAGS_STATIC_BUILTIN:
|
|
95
99
|
return True
|
|
100
|
+
return False
|
|
96
101
|
else:
|
|
97
102
|
return True
|
|
98
103
|
#if cls in vars(builtins).values() or cls in vars(types).values():
|
|
99
104
|
# return True
|
|
100
105
|
#return False
|
|
101
106
|
|
|
102
|
-
def proxyCls(
|
|
107
|
+
def proxyCls(T=_EmptyTarget, chain=EMPTY_OBJ, fromvar=None):
|
|
103
108
|
# 泛型,proxyCls(T)生成类型信息为T的继承自ProxiedObj的类
|
|
104
|
-
if
|
|
109
|
+
if T is EMPTY_OBJ:
|
|
105
110
|
return ProxiedObj
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
class ProxyCls(ProxiedObj): # 创建同时从ProxiedObj与target_obj继承的类
|
|
111
|
+
|
|
112
|
+
class ProxyCls(ProxiedObj): # 创建同时从ProxiedObj与T继承的类
|
|
113
|
+
# pylint: disable=unused-private-member
|
|
110
114
|
__proto = T # 自身使用的原型
|
|
115
|
+
def __new__(cls,*args,**kw):
|
|
116
|
+
_chain = args[0] if args else kw.get("chain")
|
|
117
|
+
if not _isinstance(_chain,ObjChain) and chain is not EMPTY_OBJ:
|
|
118
|
+
var = chain.new_var(T.__name__)
|
|
119
|
+
if fromvar is not None:
|
|
120
|
+
# pylint: disable=self-cls-assignment
|
|
121
|
+
cls = chain.add_existing_obj(T,var,f"{var} = {fromvar}.__class__",
|
|
122
|
+
[fromvar])
|
|
123
|
+
else:
|
|
124
|
+
cls = chain.add_existing_obj(T,var, # pylint: disable=self-cls-assignment
|
|
125
|
+
f"# class {var}: {shortrepr(cls,repr_func=basic_repr)}")
|
|
126
|
+
if len(args) == 3 and isinstance(T,type):
|
|
127
|
+
args=(args[0],tuple(unproxy_obj(cls) for cls in args[1]),args[2])
|
|
128
|
+
|
|
129
|
+
if kw:return cls(*args,**kw)
|
|
130
|
+
return cls(*args) # 返回ProxyCls
|
|
131
|
+
else:
|
|
132
|
+
return ProxiedObj.__new__(cls,*args,**kw)
|
|
111
133
|
|
|
112
134
|
return ProxyCls
|
|
113
135
|
|
|
@@ -148,7 +170,7 @@ class ObjChain:
|
|
|
148
170
|
# 自动检测依赖的变量,返回变量名的列表和未知对象的列表
|
|
149
171
|
result = []; unknown = []
|
|
150
172
|
for obj in itertools.chain(*iterables):
|
|
151
|
-
if
|
|
173
|
+
if _isinstance(obj,ProxiedObj):
|
|
152
174
|
self._assert_assoc_with(obj) # 确保关联到自身
|
|
153
175
|
result.append(obj._ProxiedObj__name)
|
|
154
176
|
elif id(obj) in self.exported_vars:
|
|
@@ -192,8 +214,8 @@ class ObjChain:
|
|
|
192
214
|
else:
|
|
193
215
|
exec(statement,self.scope)
|
|
194
216
|
result = self.scope[name]
|
|
195
|
-
return proxyCls(result)(self,name,
|
|
196
|
-
|
|
217
|
+
return proxyCls(type(result),self,name)(self,name,result,
|
|
218
|
+
export_trivial_obj=export_trivial_obj)
|
|
197
219
|
def add_existing_obj(self,obj,name,statement=None,dependency_vars=None,
|
|
198
220
|
export_funcs=None,export_attrs=None,extra_info=None,
|
|
199
221
|
export_trivial_obj=None,_export_call=False,
|
|
@@ -213,8 +235,8 @@ class ObjChain:
|
|
|
213
235
|
self.custom_export_attr_check[name] = custom_export_attr
|
|
214
236
|
|
|
215
237
|
self.add_code(statement,name,dependency_vars,**(extra_info or {}))
|
|
216
|
-
self.scope[name] = proxyCls(obj)(self, name,
|
|
217
|
-
|
|
238
|
+
self.scope[name] = proxyCls(type(obj),self,name)(self, name, obj, _export_call,
|
|
239
|
+
export_trivial_obj = export_trivial_obj)
|
|
218
240
|
return self.scope[name]
|
|
219
241
|
def add_exported_obj(self,obj,name): # 添加已导出的对象,仅更新exported_vars
|
|
220
242
|
self.exported_vars[id(obj)] = name
|
|
@@ -232,7 +254,7 @@ class ObjChain:
|
|
|
232
254
|
if obj._ProxiedObj__chain is not self:
|
|
233
255
|
raise ValueError("chain.get_repr(obj): obj is not associated with this chain")
|
|
234
256
|
def get_target(self,obj): # 获取ProxiedObj的目标对象
|
|
235
|
-
if not
|
|
257
|
+
if not _isinstance(obj,ProxiedObj):
|
|
236
258
|
raise TypeError("obj should be an instance of ProxiedObj")
|
|
237
259
|
self._assert_assoc_with(obj)
|
|
238
260
|
target = obj._ProxiedObj__target_obj
|
|
@@ -258,7 +280,7 @@ class ObjChain:
|
|
|
258
280
|
return varname
|
|
259
281
|
num += 1
|
|
260
282
|
def get_repr(self,obj): # 用于代码生成中的repr,如果对象是ProxiedObj,则直接返回对应的变量名
|
|
261
|
-
if
|
|
283
|
+
if _isinstance(obj,ProxiedObj):
|
|
262
284
|
self._assert_assoc_with(obj)
|
|
263
285
|
return obj._ProxiedObj__name
|
|
264
286
|
else:
|
|
@@ -355,7 +377,7 @@ class ObjChain:
|
|
|
355
377
|
if export or use_exported_obj: # use_exported_obj: 是否在不导出时也在exec用target_obj作为变量,避免递归
|
|
356
378
|
scope = {}
|
|
357
379
|
for var in self.code_vars[cur_idx][1]: # 导出时,不使用ProxiedObj执行,避免递归
|
|
358
|
-
if
|
|
380
|
+
if _isinstance(self.scope[var],ProxiedObj):
|
|
359
381
|
self._assert_assoc_with(self.scope[var])
|
|
360
382
|
scope[var] = self.scope[var]._ProxiedObj__target_obj
|
|
361
383
|
else:
|
|
@@ -443,8 +465,8 @@ def magic_meth_chained(fmt = None, use_newvar = True, indent_delta = 0,
|
|
|
443
465
|
result = chain.eval_value(new_var) # 逐行一次性执行代码,并返回结果
|
|
444
466
|
return result
|
|
445
467
|
if use_newvar:
|
|
446
|
-
return proxyCls(result)(chain,new_var,
|
|
447
|
-
|
|
468
|
+
return proxyCls(type(result),self._ProxiedObj__chain,new_var)(chain,new_var,
|
|
469
|
+
result,export_trivial_obj=self._ProxiedObj__export_trivial_obj)
|
|
448
470
|
return self
|
|
449
471
|
|
|
450
472
|
return override
|
|
@@ -454,10 +476,12 @@ class ProxiedObj:
|
|
|
454
476
|
# 代理其他对象的类(建议使用ObjChain的new_object和add_existing_obj方法,而不是实例化本类)
|
|
455
477
|
# 如果有target_obj,则内部应使用泛型proxyCls(T)替代ProxiedObj类,
|
|
456
478
|
# 避免isinstance检测返回False
|
|
457
|
-
def __init__(self,chain,name,
|
|
458
|
-
|
|
479
|
+
def __init__(self,chain,name,target_obj=EMPTY_OBJ,
|
|
480
|
+
_export_call=False,export_trivial_obj=False):
|
|
459
481
|
# target_obj: 要操作(代理)的目标对象,可选
|
|
460
482
|
# _export_call: 当前对象的__call__是否会导出真正的结果(而不是下一个ProxiedObj)
|
|
483
|
+
if "_ProxiedObj__chain" in object.__getattribute__(self,"__dict__"):
|
|
484
|
+
return # 已经初始化过
|
|
461
485
|
self.__chain=chain
|
|
462
486
|
self.__name=name
|
|
463
487
|
self.__export_call=_export_call
|
|
@@ -472,6 +496,7 @@ class ProxiedObj:
|
|
|
472
496
|
self.__chain.proxies[id(target_obj)] = self
|
|
473
497
|
|
|
474
498
|
self.__export_trivial_obj=export_trivial_obj
|
|
499
|
+
# pylint: disable=unused-private-member
|
|
475
500
|
self.__no_self_attr = True # 不再使用自身的属性
|
|
476
501
|
def __call__(self,*args,**kw):
|
|
477
502
|
depend_vars = self.__chain._detect_var_and_add_obj((self,), args, kw.values())
|
|
@@ -490,8 +515,7 @@ class ProxiedObj:
|
|
|
490
515
|
return self.__target_obj(*args,**kw)
|
|
491
516
|
else:
|
|
492
517
|
return self.__target_obj(*args) # 避免对不接收关键字参数的函数传递关键字
|
|
493
|
-
result = self.__chain._get_new_targetobj(self.__target_obj,new_var,_getter
|
|
494
|
-
use_exported_obj=True)
|
|
518
|
+
result = self.__chain._get_new_targetobj(self.__target_obj,new_var,_getter)
|
|
495
519
|
|
|
496
520
|
if self.__export_call or (self.__export_trivial_obj
|
|
497
521
|
and is_trivial_obj(result)):
|
|
@@ -506,9 +530,8 @@ class ProxiedObj:
|
|
|
506
530
|
if self.__name in self.__chain.export_funcs:
|
|
507
531
|
self.__chain.export_funcs[new_var] = self.__chain.\
|
|
508
532
|
export_funcs[self.__name].copy()
|
|
509
|
-
return proxyCls(result)(self.__chain,new_var,
|
|
510
|
-
|
|
511
|
-
export_trivial_obj=self.__export_trivial_obj)
|
|
533
|
+
return proxyCls(type(result),self.__chain,self.__name)(self.__chain,new_var,
|
|
534
|
+
result,export_trivial_obj=self.__export_trivial_obj)
|
|
512
535
|
|
|
513
536
|
#@magic_meth_chained("{_var} = {_self}.{}")
|
|
514
537
|
def __getattr__(self,attr):
|
|
@@ -537,9 +560,10 @@ class ProxiedObj:
|
|
|
537
560
|
else:
|
|
538
561
|
self.__chain.update_exports(self.__name, attr, new_var)
|
|
539
562
|
|
|
540
|
-
return proxyCls(result)
|
|
541
|
-
|
|
542
|
-
|
|
563
|
+
return proxyCls(type(result),self.__chain,self.__name)(
|
|
564
|
+
self.__chain,new_var,result,
|
|
565
|
+
self.__chain.is_export_func(attr,self.__name),
|
|
566
|
+
self.__export_trivial_obj)
|
|
543
567
|
@magic_meth_chained("{_var} = str({_self})",export=True)
|
|
544
568
|
def __str__(self): pass
|
|
545
569
|
@magic_meth_chained("{_var} = repr({_self})",export=True)
|
|
@@ -561,6 +585,16 @@ class ProxiedObj:
|
|
|
561
585
|
and not attr.startswith("_ProxiedObj"):
|
|
562
586
|
raise AttributeError # 改用__getattr__
|
|
563
587
|
return result
|
|
588
|
+
def __new__(cls,*args,**kw):
|
|
589
|
+
if len(args) >= 3:
|
|
590
|
+
target_obj = args[2]
|
|
591
|
+
elif "target_obj" in kw:
|
|
592
|
+
target_obj = kw["target_obj"]
|
|
593
|
+
else:
|
|
594
|
+
target_obj = EMPTY_OBJ
|
|
595
|
+
if _isinstance(target_obj, ProxiedObj):
|
|
596
|
+
return target_obj # 避免重复包装对象,提高性能
|
|
597
|
+
return object.__new__(cls)
|
|
564
598
|
|
|
565
599
|
# 算术运算符
|
|
566
600
|
@magic_meth_chained("{_var} = {_self} + {!r}")
|
|
@@ -712,7 +746,8 @@ class ProxiedObj:
|
|
|
712
746
|
@magic_meth_chained(default_fmt=True,export=True)
|
|
713
747
|
def __fspath__(self):pass
|
|
714
748
|
|
|
715
|
-
|
|
749
|
+
_isinstance = isinstance
|
|
750
|
+
from pyobject.objproxy.builtin_hook import hook_builtins # pylint: disable=ungrouped-imports
|
|
716
751
|
hook_builtins() # hook内置函数
|
|
717
752
|
|
|
718
753
|
def proxy_demo():
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#
|
|
2
|
-
import builtins,_collections_abc
|
|
1
|
+
# 内置函数和部分标准库函数的hook
|
|
2
|
+
import sys,builtins,_collections_abc,inspect
|
|
3
3
|
import collections.abc as collections_abc
|
|
4
4
|
from pyobject.objproxy import ProxiedObj,accept_raw_obj
|
|
5
5
|
|
|
@@ -14,7 +14,7 @@ def __build_class__(func, cls_name, *bases, metaclass=None, **kwds):
|
|
|
14
14
|
chain = None
|
|
15
15
|
dependency = []
|
|
16
16
|
for i, cls in enumerate(bases):
|
|
17
|
-
if
|
|
17
|
+
if _isinstance(cls, ProxiedObj):
|
|
18
18
|
bases[i] = cls._ProxiedObj__target_obj
|
|
19
19
|
if chain is not None and cls._ProxiedObj__chain is not chain:
|
|
20
20
|
raise ValueError("base classes should be associated with the same chain")
|
|
@@ -40,7 +40,7 @@ _getattr = getattr
|
|
|
40
40
|
def isinstance(obj, class_or_tuple):
|
|
41
41
|
if _isinstance(class_or_tuple, ProxiedObj):
|
|
42
42
|
class_or_tuple = class_or_tuple._ProxiedObj__target_obj
|
|
43
|
-
|
|
43
|
+
elif _isinstance(class_or_tuple, tuple):
|
|
44
44
|
class_or_tuple = tuple(cls._ProxiedObj__target_obj \
|
|
45
45
|
if _isinstance(cls, ProxiedObj) else cls \
|
|
46
46
|
for cls in class_or_tuple)
|
|
@@ -52,6 +52,8 @@ def isinstance(obj, class_or_tuple):
|
|
|
52
52
|
return _isinstance(obj, class_or_tuple) # 重新调用_isinstance
|
|
53
53
|
return result
|
|
54
54
|
def issubclass(cls, class_or_tuple):
|
|
55
|
+
if _isinstance(cls, ProxiedObj):
|
|
56
|
+
cls = cls._ProxiedObj__target_obj
|
|
55
57
|
if _isinstance(class_or_tuple, ProxiedObj):
|
|
56
58
|
class_or_tuple = class_or_tuple._ProxiedObj__target_obj
|
|
57
59
|
if _issubclass(cls, ProxiedObj):
|
|
@@ -64,9 +66,9 @@ def callable(obj):
|
|
|
64
66
|
obj = obj._ProxiedObj__target_obj
|
|
65
67
|
return _callable(obj)
|
|
66
68
|
def getattr(*args,**kw):
|
|
67
|
-
if
|
|
69
|
+
if _isinstance(args[1],ProxiedObj):
|
|
68
70
|
args = (args[0], args[1]._ProxiedObj__target_obj, *args[2:])
|
|
69
|
-
if "name" in kw and
|
|
71
|
+
if "name" in kw and _isinstance(kw["name"],ProxiedObj):
|
|
70
72
|
kw["name"] = kw["name"]._ProxiedObj__target_obj
|
|
71
73
|
return _getattr(*args)
|
|
72
74
|
|
|
@@ -79,6 +81,51 @@ def _check_methods(Cls, *methods):
|
|
|
79
81
|
return _pre_check_methods(object,*methods) # ProxiedObj继承自object
|
|
80
82
|
return _pre_check_methods(Cls,*methods)
|
|
81
83
|
|
|
84
|
+
_super=builtins.super
|
|
85
|
+
class super(_super):
|
|
86
|
+
def __init__(self,*args):
|
|
87
|
+
# 模拟CPython typeobject.c中super()的行为
|
|
88
|
+
if not args:
|
|
89
|
+
frame = getattr(sys._getframe(),"f_back",None)
|
|
90
|
+
if frame is None:
|
|
91
|
+
raise RuntimeError("super(): no current frame")
|
|
92
|
+
code = frame.f_code
|
|
93
|
+
if code is None:
|
|
94
|
+
raise RuntimeError("super(): no code object")
|
|
95
|
+
if code.co_argcount == 0:
|
|
96
|
+
raise RuntimeError("super(): no arguments")
|
|
97
|
+
try:
|
|
98
|
+
self_ = frame.f_locals[code.co_varnames[0]]
|
|
99
|
+
except (IndexError, KeyError):
|
|
100
|
+
raise RuntimeError("super(): arg[0] deleted") from None
|
|
101
|
+
if "__class__" not in frame.f_locals:
|
|
102
|
+
raise RuntimeError("super(): bad __class__ cell")
|
|
103
|
+
cls = frame.f_locals["__class__"]
|
|
104
|
+
if not isinstance(cls, type):
|
|
105
|
+
raise RuntimeError("super(): __class__ is not a type (%s)" \
|
|
106
|
+
% type(cls).__name__)
|
|
107
|
+
args = (cls, self_)
|
|
108
|
+
args = tuple(item._ProxiedObj__target_obj if _issubclass(
|
|
109
|
+
object.__getattribute__(item,"__class__"),
|
|
110
|
+
ProxiedObj) else item for item in args) # 确保不为ProxiedObj
|
|
111
|
+
_super.__init__(self,*args)
|
|
112
|
+
|
|
113
|
+
_signature = inspect.signature
|
|
114
|
+
_getattr_static = inspect.getattr_static
|
|
115
|
+
def signature(obj, **kw):
|
|
116
|
+
if _isinstance(obj, ProxiedObj):
|
|
117
|
+
obj = obj._ProxiedObj__target_obj
|
|
118
|
+
return _signature(obj, **kw)
|
|
119
|
+
def getattr_static(obj, attr, *args, **kw):
|
|
120
|
+
if _isinstance(obj, ProxiedObj):
|
|
121
|
+
obj = obj._ProxiedObj__target_obj
|
|
122
|
+
if _isinstance(attr, ProxiedObj):
|
|
123
|
+
attr = attr._ProxiedObj__target_obj
|
|
124
|
+
if kw:
|
|
125
|
+
return _getattr_static(obj, attr, *args, **kw)
|
|
126
|
+
else:
|
|
127
|
+
return _getattr_static(obj, attr, *args)
|
|
128
|
+
|
|
82
129
|
def hook_builtins():
|
|
83
130
|
builtins.range=range
|
|
84
131
|
builtins.__build_class__ = __build_class__
|
|
@@ -86,8 +133,11 @@ def hook_builtins():
|
|
|
86
133
|
builtins.issubclass = issubclass
|
|
87
134
|
builtins.callable = callable
|
|
88
135
|
builtins.getattr = getattr
|
|
136
|
+
builtins.super = super
|
|
89
137
|
_collections_abc._check_methods = _check_methods # 修改collections.abc库
|
|
90
138
|
collections_abc._check_methods = _check_methods
|
|
139
|
+
inspect.signature = signature
|
|
140
|
+
inspect.getattr_static = getattr_static
|
|
91
141
|
|
|
92
142
|
# 内置类型的修改(备用)
|
|
93
143
|
class CustomStr(builtins.str):
|
|
@@ -82,7 +82,7 @@ def ck(obj,symbol):
|
|
|
82
82
|
def magic_meth(meth): # 用于无法获知具体表达式的魔法方法调用(备用函数)
|
|
83
83
|
@functools.wraps(meth)
|
|
84
84
|
def override(self,*args,**kw):
|
|
85
|
-
return DynObj(f"{ck(self)}.{meth.__name__}") # 优先级使用默认的HIGHEST
|
|
85
|
+
return DynObj(f"{ck(self, HIGHEST)}.{meth.__name__}") # 优先级使用默认的HIGHEST
|
|
86
86
|
return override
|
|
87
87
|
|
|
88
88
|
# 能包装表达式链式求值的对象(不可变)
|
|
@@ -111,9 +111,9 @@ class DynObj:
|
|
|
111
111
|
return self.__code
|
|
112
112
|
|
|
113
113
|
# 算术运算符
|
|
114
|
-
def __add__(self, other): return DynObj(f"{ck(self,ADD)} + {ck(other,ADD)}",ADD)
|
|
115
|
-
def __sub__(self, other): return DynObj(f"{ck(self,SUB)} - {ck(other,SUB)}",SUB)
|
|
116
|
-
def __mul__(self, other): return DynObj(f"{ck(self,MUL)} * {ck(other,MUL)}",MUL)
|
|
114
|
+
def __add__(self, other): return DynObj(f"{ck(self, ADD)} + {ck(other,ADD)}",ADD)
|
|
115
|
+
def __sub__(self, other): return DynObj(f"{ck(self, SUB)} - {ck(other,SUB)}",SUB)
|
|
116
|
+
def __mul__(self, other): return DynObj(f"{ck(self, MUL)} * {ck(other,MUL)}",MUL)
|
|
117
117
|
def __truediv__(self, other):
|
|
118
118
|
return DynObj(f"{ck(self, DIV)} / {ck(other, DIV)}", DIV)
|
|
119
119
|
def __floordiv__(self, other):
|
|
@@ -4,10 +4,10 @@ from pyobject.objproxy.utils import *
|
|
|
4
4
|
MERGABLE_INFO = ["_alias_name"]
|
|
5
5
|
|
|
6
6
|
class Statement: # 图节点(一条语句)
|
|
7
|
-
def __init__(self,graph,code,
|
|
7
|
+
def __init__(self,graph,code,varname,dependency_vars,extra_info=None):
|
|
8
8
|
self.graph = graph
|
|
9
9
|
self.code = code
|
|
10
|
-
self.var =
|
|
10
|
+
self.var = varname
|
|
11
11
|
self.extra_info = extra_info or {}
|
|
12
12
|
self.removed = False
|
|
13
13
|
self.depends = set()
|
|
@@ -39,7 +39,8 @@ class Statement: # 图节点(一条语句)
|
|
|
39
39
|
self.var = None
|
|
40
40
|
except NotAssignmentError:pass # 不是赋值语句
|
|
41
41
|
if remove_internal and self.extra_info.get("_internal",False)\
|
|
42
|
-
|
|
42
|
+
or remove_export_type and self.extra_info.get("_export_type",False)\
|
|
43
|
+
or self.extra_info.get("_optional_stat",False):
|
|
43
44
|
for dep in self.depends:
|
|
44
45
|
dep.affects.remove(self)
|
|
45
46
|
dep.affects_cnt -= 1
|
|
@@ -100,7 +101,7 @@ def import_optimizer(graph):
|
|
|
100
101
|
try:
|
|
101
102
|
stat.code = optimize_import(stat.code)
|
|
102
103
|
except NotAnImportError:pass
|
|
103
|
-
|
|
104
|
+
|
|
104
105
|
graph.clear_removed_statements()
|
|
105
106
|
|
|
106
107
|
def import_alias_optimizer(graph):
|
|
@@ -153,4 +154,4 @@ unused_var = submod.func(temp_var)"""
|
|
|
153
154
|
[("submod",[]),("temp_var",[]),
|
|
154
155
|
("unused_var",["submod","temp_var"])]), sep="")
|
|
155
156
|
|
|
156
|
-
if __name__=="__main__":test()
|
|
157
|
+
if __name__=="__main__":test()
|
|
@@ -62,7 +62,7 @@ class TestObjChain(unittest.TestCase):
|
|
|
62
62
|
class Cls2:pass
|
|
63
63
|
chain = ObjChain()
|
|
64
64
|
obj = chain.add_existing_obj(Cls(),"obj")
|
|
65
|
-
self.assertTrue(issubclass(type(obj), Cls))
|
|
65
|
+
self.assertTrue(issubclass(type(obj), Cls))
|
|
66
66
|
self.assertFalse(issubclass(type(obj), Cls2))
|
|
67
67
|
self.assertTrue(isinstance(obj, Cls))
|
|
68
68
|
self.assertFalse(isinstance(obj, Cls2))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from time import perf_counter
|
|
2
|
+
import random
|
|
3
|
+
try:
|
|
4
|
+
from pyobject import ObjChain, ProxiedObj
|
|
5
|
+
except ImportError:
|
|
6
|
+
path = __file__
|
|
7
|
+
for i in range(3):
|
|
8
|
+
path = os.path.split(path)[0]
|
|
9
|
+
sys.path.append(path) # 加入当前pyobject库所在的目录
|
|
10
|
+
from pyobject import ObjChain, ProxiedObj
|
|
11
|
+
|
|
12
|
+
REPEAT_TIMES = 20000
|
|
13
|
+
|
|
14
|
+
def test_perf(use_objchain=True):
|
|
15
|
+
chain = ObjChain(export_trivial_obj=False)
|
|
16
|
+
class Cls:
|
|
17
|
+
def __init__(self, value=None):
|
|
18
|
+
self.attr = value
|
|
19
|
+
def meth(self):
|
|
20
|
+
return self.attr
|
|
21
|
+
__call__ = meth
|
|
22
|
+
def __add__(self, other):
|
|
23
|
+
return Cls(other)
|
|
24
|
+
|
|
25
|
+
if use_objchain:
|
|
26
|
+
Cls = chain.add_existing_obj(Cls,"Cls")
|
|
27
|
+
|
|
28
|
+
obj1, obj2 = Cls(1), Cls(42)
|
|
29
|
+
obj1.attr, obj2.attr = obj2, obj1
|
|
30
|
+
start = perf_counter()
|
|
31
|
+
for _ in range(REPEAT_TIMES):
|
|
32
|
+
op = random.randint(0,3)
|
|
33
|
+
if op == 0:
|
|
34
|
+
new_obj = obj1 + obj2
|
|
35
|
+
obj1, obj2 = obj2, new_obj
|
|
36
|
+
elif op == 1:
|
|
37
|
+
new_obj = obj1.attr
|
|
38
|
+
obj1, obj2 = obj2, new_obj
|
|
39
|
+
elif op == 2:
|
|
40
|
+
new_obj = obj1() #obj1.meth()
|
|
41
|
+
obj1, obj2 = obj2, new_obj
|
|
42
|
+
elif op == 3:
|
|
43
|
+
obj1.attr = obj1
|
|
44
|
+
print(f"use_objchain={use_objchain}: {perf_counter()-start:.9f}s")
|
|
45
|
+
|
|
46
|
+
if __name__=="__main__":
|
|
47
|
+
print(f"REPEAT_TIMES={REPEAT_TIMES}")
|
|
48
|
+
test_perf(True)
|
|
49
|
+
test_perf(False)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyobject
|
|
3
|
-
Version: 1.2.9
|
|
3
|
+
Version: 1.2.9.2
|
|
4
4
|
Summary: A multifunctional all-in-one utility tool for managing internal Python objects, compatible with nearly all Python 3 versions. 一个多功能合一的提供操作Python对象底层工具的Python包, 支持几乎所有Python 3版本。
|
|
5
5
|
Home-page: https://github.com/qfcy/pyobject
|
|
6
6
|
Author: qfcy
|
|
@@ -351,7 +351,7 @@ Python内部的字节码对象\ ``CodeType``\ ,如\ ``func.__code__``\ ,是
|
|
|
351
351
|
unused_var = func(temp_var)
|
|
352
352
|
|
|
353
353
|
| 代码中\ ``temp_var``\ 只有一条指向\ ``unused_var``\ 的边,而\ ``unused_var``\ 没有任何指出的边。
|
|
354
|
-
| 将\ ``temp_var``\ 的值代入\ ``func(temp_var)``\
|
|
354
|
+
| 将\ ``temp_var``\ 的值代入\ ``func(temp_var)``\ 进行内联,得到\ ``unused_var = func([1,2,3])``\ ,再去掉\ ``unused_var``\ ,优化后的代码会变成\ ``func([1,2,3])``\ 。
|
|
355
355
|
|
|
356
356
|
模块\ ``pyobject.pyobj_extension``
|
|
357
357
|
----------------------------------
|
|
@@ -394,7 +394,7 @@ Python内部的字节码对象\ ``CodeType``\ ,如\ ``func.__code__``\ ,是
|
|
|
394
394
|
lst调用多次==运算符(``__eq__``)相比,
|
|
395
395
|
本函数直接比较对象的指针,提高了效率。
|
|
396
396
|
|
|
397
|
-
**``pyobject``\ 当前版本**: 1.2.9
|
|
397
|
+
**``pyobject``\ 当前版本**: 1.2.9.2
|
|
398
398
|
|
|
399
399
|
更新日志:
|
|
400
400
|
---------
|
|
@@ -829,9 +829,9 @@ Implementation Details
|
|
|
829
829
|
|
|
830
830
|
| Here, ``temp_var`` only has one edge pointing to ``unused_var``, while
|
|
831
831
|
``unused_var`` does not point to any other node.
|
|
832
|
-
| By inlining the value of ``temp_var`` into ``func(temp_var)
|
|
833
|
-
|
|
834
|
-
``func([1, 2, 3])``.
|
|
832
|
+
| By inlining the value of ``temp_var`` into ``func(temp_var)``, the
|
|
833
|
+
code becomes ``unused_var = func([1,2,3])``. After removing
|
|
834
|
+
``unused_var``, the optimized code is ``func([1, 2, 3])``.
|
|
835
835
|
|
|
836
836
|
Module: ``pyobj_extension``
|
|
837
837
|
---------------------------
|
|
@@ -886,7 +886,7 @@ Python call “obj in lst” that invokes the “==” operator (``__eq__``)
|
|
|
886
886
|
multiple times, this function directly compares the pointers to improve
|
|
887
887
|
efficiency.
|
|
888
888
|
|
|
889
|
-
**Current Version of ``pyobject``**: 1.2.9
|
|
889
|
+
**Current Version of ``pyobject``**: 1.2.9.2
|
|
890
890
|
|
|
891
891
|
Change Log
|
|
892
892
|
----------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|