μ΄ μμ μ€ν¬λ¦½νΈλ‘ λ²μλ₯Ό μ’νκ³ μμ§ νκ³ λ€μ§λ μμμ§λ§ ν‘μ° μ΄μ μλ‘μ΄ κΈ°λ₯ μ»΄νμΌ νλͺ©μ λλ€.
from werkzeug.routing import Map, Rule
def main():
while True:
Map([Rule('/a/<string:b>')])
if __name__ == '__main__':
exit(main())
@ edk0
κ·μΉμ΄ μ¬λ°λ₯΄κ² GCλμ§ μλλ€κ³ λ§νλ κ² κ°μ΅λκΉ?
μ΄λ€ μ€μ μν©μμ μ΄λ° μΌμ΄ λ°μνλμ§ νμ€νμ§ μμ΅λλ€. μΌλ°μ μΌλ‘ κ·μΉ μ§ν©μ μ μν λ€μ μ¬μ©νλ©° μμλ‘ μμ± λ° μμ λμ§ μμ΅λλ€.
μ¬κΈ°μ μ°λ¦¬μ μ¬μ©λ²μ κ΅¬μ± νμΌμ μ μλ μΌλ ¨μ 리λλ μ μ ν¬ν¨ν©λλ€. μ΄ κ΅¬μ± νμΌμ κ°μ κ²½λ‘λ₯Ό μΆκ°νκ±°λ μ κ±°ν λ μ£ΌκΈ°μ μΌλ‘ λ³κ²½λ©λλ€.
λ² λν° λΌμ°νΈλ₯Ό μΆκ°ν λλ§λ€ μ ν리μΌμ΄μ
μ λ°°ν¬νλ λμ κ΅¬μ± νμΌμ μ
λ°μ΄νΈνκΈ°λ§ νλ©΄ μ ν리μΌμ΄μ
μ΄ λΌμ°νΈ 맀νμ λ€μ λΉλν©λλ€(μ΄λ flask
μ±μμ 리λλ μ
μ μ 곡νλ λ° μ¬μ©λ¨).
ν , 맡μ λν λ³κ²½ μ¬νμ΄ λ€μ€ νλ‘μΈμ€ μμ μμμ λκΈ°νλμ§ μκΈ° λλ¬Έμ μΌλ°μ μΌλ‘ κΆμ₯λμ§ μμ΅λλ€. 리λλ μ μ΄ λμ λ°νλμ΄μΌ νλμ§ νμΈνκΈ° μν΄ 404μ λν μ€λ₯ μ²λ¦¬κΈ°λ‘ λμ ꡬνν κ²μ λλ€. μμ λμ΄μλ μλλ€λ κ²μ΄ μλλΌ μ κ° λ€μ΄λ³Έ μ¬μ© μ¬λ‘κ° μλλΌλ κ²μ λλ€.
κ° κ°λ³ μμ μλ κ΅¬μ± νμΌμ μ£ΌκΈ°μ μΌλ‘ νμΈνκ³ λ€μ λ‘λν©λλ€. λ΄κ° λ§ν μ μλ ν νλΌμ€ν¬ μ±μ μ§μ μ£Όμ λμ§ μμ΅λλ€.
μ΄λ μͺ½μ΄λ , μ΄λ¬ν κ°μ²΄λ λμΆλμ΄μλ μ λ©λλ€. μμ: -- μμΈμ μ°Ύκ³ μμ΅λλ€. -- μ»΄νμΌ μ€μΈ ν¨μμ λ¬Έμ κ° μκ±°λ ν΄λΉ ν¨μμ ν΄μ±μ΄ μλ κ²μΌλ‘ μμ¬λ©λλ€( Map
μ΄λ€ μ μΌλ‘λ μ°Έμ¬νκΈ° μν΄)
μ°Έκ³ λ‘ μ΄ λμλ <string:b>
λΆλΆ μμ΄λ λ°μνμ§ μμ΅λλ€.
μμ±νλ ν¨μ κ°μ²΄μ λΆν΄λ λ€μκ³Ό κ°μ΅λλ€.
>>> x = Rule('/a/<string:b>')
>>> from werkzeug.routing import Map
>>> y = Map([x])
>>> x._build
<function <builder:'/a/<string:b>'> at 0x7f16d62d1730>
>>> import dis
>>> dis.dis(x._build)
1 0 LOAD_CONST 0 ('')
2 LOAD_CONST 1 ('/a/')
4 LOAD_CONST 2 (<bound method BaseConverter.to_url of <werkzeug.routing.UnicodeConverter object at 0x7f16d2c9a0f0>>)
6 LOAD_FAST 0 (b)
8 CALL_FUNCTION 1
10 BUILD_STRING 2
12 BUILD_TUPLE 2
14 RETURN_VALUE
>>> dis.dis(x._build_unknown)
1 0 LOAD_CONST 0 ('')
2 LOAD_CONST 1 ('/a/')
4 LOAD_CONST 2 (<bound method BaseConverter.to_url of <werkzeug.routing.UnicodeConverter object at 0x7f16d2c9a0f0>>)
6 LOAD_FAST 0 (b)
8 CALL_FUNCTION 1
10 LOAD_FAST 1 (.keyword_arguments)
12 JUMP_IF_TRUE_OR_POP 20
14 LOAD_CONST 0 ('')
16 DUP_TOP
18 JUMP_FORWARD 10 (to 30)
>> 20 LOAD_CONST 3 (functools.partial(<function url_encode at 0x7f16d2d5d510>, charset='utf-8', sort=False, key=None))
22 ROT_TWO
24 CALL_FUNCTION 1
26 LOAD_CONST 4 ('?')
28 ROT_TWO
>> 30 BUILD_STRING 4
32 BUILD_TUPLE 2
34 RETURN_VALUE
μ€ν¬λ¦½νΈλ₯Ό μ½κ° μ‘°μ :
import collections
import gc
import pprint
from werkzeug.routing import Map, Rule
def main():
for _ in range(10000):
Map([Rule('/a/<string:b>')])
for _ in range(5):
gc.collect()
counts = collections.Counter(type(o) for o in gc.get_objects())
pprint.pprint(counts.most_common(15))
if __name__ == '__main__':
exit(main())
Map
, Rule
λΏλ§ μλλΌ functools.partial
λ° UnicodeConverter
:
$ ./venv/bin/python t.py
[(<class 'dict'>, 62085),
(<class 'list'>, 50514),
(<class 'function'>, 24085),
(<class 'tuple'>, 21811),
(<class 'method'>, 20032),
(<class 'set'>, 10518),
(<class 'functools.partial'>, 10002),
(<class 'werkzeug.routing.UnicodeConverter'>, 10000),
(<class 'werkzeug.routing.Map'>, 10000),
(<class 'werkzeug.routing.Rule'>, 10000),
(<class 'weakref'>, 1306),
(<class 'wrapper_descriptor'>, 1131),
(<class 'method_descriptor'>, 879),
(<class 'builtin_function_or_method'>, 839),
(<class 'getset_descriptor'>, 740)]
λ€μμ gcμμ μ΄κ²μ μ μ§νλ λͺ κ°μ§ κ·Έλνμ λλ€.
def graph(obj, ids, *, seen=None, indent='', limit=10):
if seen is None:
seen = set()
for referrer in gc.get_referrers(obj):
# the main frame which has a hard reference to ths object
if (
type(referrer).__name__ == 'frame' and
referrer.f_globals['__name__'] == '__main__'
):
continue
# objects only present due to traversal of gc referrers
elif id(referrer) not in ids:
continue
elif id(referrer) in seen:
print(f'{indent}(already seen) {id(referrer)}')
continue
seen.add(id(referrer))
if indent == '':
print('=' * 79)
print(f'{indent}type: {type(referrer).__name__} ({id(referrer)})')
fmted = repr(referrer) #pprint.pformat(referrer)
print(indent + fmted.replace('\n', f'\n{indent}'))
if limit:
graph(
referrer, ids,
seen=seen, indent='==' + indent, limit=limit - 1,
)
...
ids = {id(o) for o in gc.get_objects()}
obj = next(iter(o for o in gc.get_objects() if isinstance(o, Map)))
graph(obj, ids)
===============================================================================
type: dict (140272699432680)
{'map': Map([<Rule '/a/<b>' -> None>]), 'regex': '[^/]{1,}'}
==type: UnicodeConverter (140272699175656)
==<werkzeug.routing.UnicodeConverter object at 0x7f93c867f2e8>
====type: method (140272750734216)
====<bound method BaseConverter.to_url of <werkzeug.routing.UnicodeConverter object at 0x7f93c867f2e8>>
======type: tuple (140272726348928)
======('', '/a/', <bound method BaseConverter.to_url of <werkzeug.routing.UnicodeConverter object at 0x7f93c867f2e8>>)
====type: method (140272749846664)
====<bound method BaseConverter.to_url of <werkzeug.routing.UnicodeConverter object at 0x7f93c867f2e8>>
======type: tuple (140272726372424)
======('', '/a/', <bound method BaseConverter.to_url of <werkzeug.routing.UnicodeConverter object at 0x7f93c867f2e8>>, functools.partial(<function url_encode at 0x7f93c877eae8>, charset='utf-8', sort=False, key=None), '?')
====type: dict (140272723298056)
===={'b': <werkzeug.routing.UnicodeConverter object at 0x7f93c867f2e8>}
======type: dict (140272749460432)
======{'rule': '/a/<string:b>', 'is_leaf': True, 'map': Map([<Rule '/a/<b>' -> None>]), 'strict_slashes': True, 'subdomain': '', 'host': None, 'defaults': None, 'build_only': False, 'alias': False, 'methods': None, 'endpoint': None, 'redirect_to': None, 'arguments': {'b'}, '_trace': [(False, '|'), (False, '/a/'), (True, 'b')], '_converters': {'b': <werkzeug.routing.UnicodeConverter object at 0x7f93c867f2e8>}, '_regex': re.compile('^\\|\\/a\\/(?P<b>[^/]{1,})$'), '_argument_weights': [100], '_static_weights': [(0, -1)], '_build': <function <builder:'/a/<string:b>'> at 0x7f93c9d880d0>, '_build_unknown': <function <builder:'/a/<string:b>'> at 0x7f93c9d88158>}
========type: Rule (140272749480984)
========<Rule '/a/<b>' -> None>
==========type: list (140272699123848)
==========[<Rule '/a/<b>' -> None>]
============type: dict (140272726280736)
============{'_rules': [<Rule '/a/<b>' -> None>], '_rules_by_endpoint': {None: [<Rule '/a/<b>' -> None>]}, '_remap': False, '_remap_lock': <unlocked _thread.lock object at 0x7f93cb6d9da0>, 'default_subdomain': '', 'charset': 'utf-8', 'encoding_errors': 'replace', 'strict_slashes': True, 'redirect_defaults': True, 'host_matching': False, 'converters': {'default': <class 'werkzeug.routing.UnicodeConverter'>, 'string': <class 'werkzeug.routing.UnicodeConverter'>, 'any': <class 'werkzeug.routing.AnyConverter'>, 'path': <class 'werkzeug.routing.PathConverter'>, 'int': <class 'werkzeug.routing.IntegerConverter'>, 'float': <class 'werkzeug.routing.FloatConverter'>, 'uuid': <class 'werkzeug.routing.UUIDConverter'>}, 'sort_parameters': False, 'sort_key': None}
==============type: Map (140272749480872)
==============Map([<Rule '/a/<b>' -> None>])
================(already seen) 140272699432680
================(already seen) 140272749460432
==========type: list (140272700110792)
==========[<Rule '/a/<b>' -> None>]
============type: dict (140272726280808)
============{None: [<Rule '/a/<b>' -> None>]}
==============(already seen) 140272726280736
(already seen) 140272749460432
μ΄κ²μ μ£ΌκΈ°λ₯Ό κΉ¨κ³ λ©λͺ¨λ¦¬ λμλ₯Ό μ κ±°νκΈ°μ μΆ©λΆν©λλ€.
diff --git a/src/werkzeug/routing.py b/src/werkzeug/routing.py
index c7cff94d..8176ddfe 100644
--- a/src/werkzeug/routing.py
+++ b/src/werkzeug/routing.py
@@ -1254,13 +1254,13 @@ class BaseConverter(object):
weight = 100
def __init__(self, map):
- self.map = map
+ self.charset = map.charset
def to_python(self, value):
return value
def to_url(self, value):
- return _fast_url_quote(text_type(value).encode(self.map.charset))
+ return _fast_url_quote(text_type(value).encode(self.charset))
class UnicodeConverter(BaseConverter):
μ¬μ΄ν΄μ μ€μ μμΈμ LOAD_CONST
μμ²΄κ° μμκ° μλ κ°μ²΄μ λν΄ μ½λ κ°μ²΄μμ μ¬μ©λκ³ μλ€λ μ μ΄λΌκ³ μκ°νμ§λ§, μ΄κ²μ΄ gcλ₯Ό νΌλμ€λ½κ² νλ€κ³ μκ°ν©λλ€(μ°Έμ‘°λ μ½λ κ°μ²΄κ° λΌμ΄λΈλΌκ³ κ°μ ν λ κ·Έλ€μ μ€μ λ‘ κ·Έλ μ§ μμ΅λλ€)
#1524λ₯Ό ν΅ν΄
κ°μ₯ μ μ©ν λκΈ
μ΄κ²μ μ£ΌκΈ°λ₯Ό κΉ¨κ³ λ©λͺ¨λ¦¬ λμλ₯Ό μ κ±°νκΈ°μ μΆ©λΆν©λλ€.
μ¬μ΄ν΄μ μ€μ μμΈμ
LOAD_CONST
μμ²΄κ° μμκ° μλ κ°μ²΄μ λν΄ μ½λ κ°μ²΄μμ μ¬μ©λκ³ μλ€λ μ μ΄λΌκ³ μκ°νμ§λ§, μ΄κ²μ΄ gcλ₯Ό νΌλμ€λ½κ² νλ€κ³ μκ°ν©λλ€(μ°Έμ‘°λ μ½λ κ°μ²΄κ° λΌμ΄λΈλΌκ³ κ°μ ν λ κ·Έλ€μ μ€μ λ‘ κ·Έλ μ§ μμ΅λλ€)