पिछले जवाब पहले से ही एक अनुरोध के दौरान फ्लास्क की पृष्ठभूमि में क्या होता है का एक अच्छा अवलोकन देते हैं। यदि आपने इसे अभी तक नहीं पढ़ा है तो मैं इसे पढ़ने से पहले @ MarkHildreth के उत्तर की सिफारिश करता हूं। संक्षेप में, प्रत्येक http अनुरोध के लिए एक नया संदर्भ (थ्रेड) बनाया गया है, यही कारण है कि थ्रेड Local
सुविधा होना आवश्यक है जो इस तरह की अनुमति देता हैrequest
औरg
धागे के पार विश्व स्तर पर सुलभ होना, जबकि उनके अनुरोध को विशिष्ट संदर्भ बनाए रखना। इसके अलावा, एक http अनुरोध संसाधित करते समय फ्लास्क भीतर से अतिरिक्त अनुरोधों का अनुकरण कर सकता है, इसलिए एक स्टैक पर उनके संबंधित संदर्भ को संग्रहीत करने की आवश्यकता है। इसके अलावा, फ्लास्क एक प्रक्रिया के भीतर कई wsgi अनुप्रयोगों को एक दूसरे के साथ चलने की अनुमति देता है, और एक से अधिक अनुरोध के दौरान कार्रवाई के लिए कहा जा सकता है (प्रत्येक अनुरोध एक नया अनुप्रयोग संदर्भ बनाता है), इसलिए अनुप्रयोगों के लिए एक संदर्भ स्टैक की आवश्यकता है। यह एक सारांश है जो पिछले उत्तरों में शामिल था।
मेरा लक्ष्य अब यह बताकर हमारी वर्तमान समझ को पूरक बनाना है कि फ्लास्क और वर्क्ज़ुग इन संदर्भों के स्थानीय लोगों के साथ क्या करते हैं। मैंने इसके तर्क की समझ को बढ़ाने के लिए कोड को सरल बनाया है, लेकिन यदि आपको यह मिलता है, तो आपको वास्तविक स्रोत ( werkzeug.local
और flask.globals
) में से अधिकांश को आसानी से समझने में सक्षम होना चाहिए ।
आइए पहले समझते हैं कि Werkzeug ने स्थानीय लोगों को कैसे लागू किया।
स्थानीय
जब कोई HTTP अनुरोध आता है, तो इसे एक ही थ्रेड के संदर्भ में संसाधित किया जाता है। एक वैकल्पिक अनुरोध के रूप में एक http संदर्भ के दौरान एक नया संदर्भ स्पॉन करने के लिए, Werkzeug भी सामान्य थ्रेड्स के बजाय ग्रीनलेट्स (एक प्रकार का हल्का "माइक्रो-थ्रेड्स") के उपयोग की अनुमति देता है। यदि आपके पास ग्रीनलेट्स स्थापित नहीं हैं, तो यह थ्रेड्स का उपयोग करने के लिए वापस आ जाएगा। इनमें से प्रत्येक थ्रेड (या ग्रीनलेट) एक अद्वितीय आईडी द्वारा पहचाने जाते हैं, जिसे आप मॉड्यूल के get_ident()
फ़ंक्शन के साथ पुनः प्राप्त कर सकते हैं । यही कारण है कि समारोह होने के पीछे जादू करने के लिए प्रारंभिक बिंदु है request
, current_app
, url_for
, g
, और अन्य ऐसी संदर्भ बाध्य वैश्विक वस्तुओं।
try:
from greenlet import get_ident
except ImportError:
from thread import get_ident
अब जब हमारे पास अपना पहचान कार्य है तो हम जान सकते हैं कि किसी भी समय हम किस थ्रेड पर हैं और हम बना सकते हैं जिसे थ्रेड कहा जाता है Local
, एक संदर्भ वस्तु जिसे वैश्विक स्तर पर एक्सेस किया जा सकता है, लेकिन जब आप इसकी विशेषताओं का उपयोग करते हैं तो वे उनके मूल्य के लिए हल करते हैं वह विशिष्ट धागा। जैसे
# globally
local = Local()
# ...
# on thread 1
local.first_name = 'John'
# ...
# on thread 2
local.first_name = 'Debbie'
दोनों मान Local
एक ही समय में वैश्विक रूप से सुलभ वस्तु पर मौजूद हैं , लेकिन local.first_name
थ्रेड 1 के संदर्भ में पहुंच आपको प्रदान करेगा 'John'
, जबकि यह 'Debbie'
थ्रेड 2 पर वापस आ जाएगा ।
वो कैसे संभव है? आइए देखें कुछ (सरलीकृत) कोड:
class Local(object)
def __init__(self):
self.storage = {}
def __getattr__(self, name):
context_id = get_ident() # we get the current thread's or greenlet's id
contextual_storage = self.storage.setdefault(context_id, {})
try:
return contextual_storage[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
context_id = get_ident()
contextual_storage = self.storage.setdefault(context_id, {})
contextual_storage[name] = value
def __release_local__(self):
context_id = get_ident()
self.storage.pop(context_id, None)
local = Local()
ऊपर दिए गए कोड से हम देख सकते हैं कि जादू उबलता है get_ident()
जिससे वर्तमान ग्रीनलेट या धागे की पहचान होती है। Local
भंडारण तो सिर्फ इतना है कि का उपयोग करता है वर्तमान धागा करने के लिए किसी भी डेटा प्रासंगिक स्टोर करने के लिए एक कुंजी के रूप।
आप एक से अधिक हो सकता है Local
प्रक्रिया के अनुसार वस्तुओं और request
, g
, current_app
और दूसरों को बस ऐसे ही बनाया जा सकता था। लेकिन यह नहीं है कि यह कैसे फ्लास्क में किया गया है जिसमें ये तकनीकी रूप से Local
ऑब्जेक्ट नहीं हैं, बल्कि अधिक सटीक LocalProxy
ऑब्जेक्ट हैं। क्या है LocalProxy
?
LocalProxy
लोकलप्रॉक्सी एक ऐसी वस्तु है जो Local
किसी अन्य वस्तु को खोजने के लिए सवाल करती है (यानी यह जिस वस्तु से जुड़ी है )। आइए समझने के लिए एक नज़र डालें:
class LocalProxy(object):
def __init__(self, local, name):
# `local` here is either an actual `Local` object, that can be used
# to find the object of interest, here identified by `name`, or it's
# a callable that can resolve to that proxied object
self.local = local
# `name` is an identifier that will be passed to the local to find the
# object of interest.
self.name = name
def _get_current_object(self):
# if `self.local` is truly a `Local` it means that it implements
# the `__release_local__()` method which, as its name implies, is
# normally used to release the local. We simply look for it here
# to identify which is actually a Local and which is rather just
# a callable:
if hasattr(self.local, '__release_local__'):
try:
return getattr(self.local, self.name)
except AttributeError:
raise RuntimeError('no object bound to %s' % self.name)
# if self.local is not actually a Local it must be a callable that
# would resolve to the object of interest.
return self.local(self.name)
# Now for the LocalProxy to perform its intended duties i.e. proxying
# to an underlying object located somewhere in a Local, we turn all magic
# methods into proxies for the same methods in the object of interest.
@property
def __dict__(self):
try:
return self._get_current_object().__dict__
except RuntimeError:
raise AttributeError('__dict__')
def __repr__(self):
try:
return repr(self._get_current_object())
except RuntimeError:
return '<%s unbound>' % self.__class__.__name__
def __bool__(self):
try:
return bool(self._get_current_object())
except RuntimeError:
return False
# ... etc etc ...
def __getattr__(self, name):
if name == '__members__':
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)
def __setitem__(self, key, value):
self._get_current_object()[key] = value
def __delitem__(self, key):
del self._get_current_object()[key]
# ... and so on ...
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
__delattr__ = lambda x, n: delattr(x._get_current_object(), n)
__str__ = lambda x: str(x._get_current_object())
__lt__ = lambda x, o: x._get_current_object() < o
__le__ = lambda x, o: x._get_current_object() <= o
__eq__ = lambda x, o: x._get_current_object() == o
# ... and so forth ...
अब विश्व स्तर पर सुलभ परदे के पीछे बनाने के लिए आप क्या करेंगे
# this would happen some time near application start-up
local = Local()
request = LocalProxy(local, 'request')
g = LocalProxy(local, 'g')
और अब कुछ समय पहले एक अनुरोध पर आप स्थानीय के अंदर कुछ वस्तुओं को संग्रहित करेंगे जो कि पहले से निर्मित परदे के पीछे पहुंच सकते हैं, कोई फर्क नहीं पड़ता कि हम किस थ्रेड पर हैं।
# this would happen early during processing of an http request
local.request = RequestContext(http_environment)
local.g = SomeGeneralPurposeContainer()
LocalProxy
उन्हें Locals
स्वयं बनाने के बजाय विश्व स्तर पर सुलभ वस्तुओं के रूप में उपयोग करने का लाभ यह है कि यह उनके प्रबंधन को सरल बनाता है । Local
विश्व स्तर पर सुलभ प्रॉक्सिज़ बनाने के लिए आपको केवल एक ही ऑब्जेक्ट की आवश्यकता है । अनुरोध के अंत में, सफाई के दौरान, आप बस एक को रिहा करते हैं Local
(यानी आप इसके भंडारण से संदर्भ_आईडी को पॉप करते हैं) और परदे के पीछे परेशान नहीं करते हैं, वे अभी भी विश्व स्तर पर सुलभ हैं और अभी भी Local
अपनी वस्तु खोजने के लिए एक को टालते हैं बाद के HTTP अनुरोधों के लिए ब्याज की।
# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()
LocalProxy
जब हमारे पास पहले से ही एक के निर्माण को सरल बनाने के लिए Local
, Werkzeug Local.__call__()
जादू की विधि को इस प्रकार लागू करता है:
class Local(object):
# ...
# ... all same stuff as before go here ...
# ...
def __call__(self, name):
return LocalProxy(self, name)
# now you can do
local = Local()
request = local('request')
g = local('g')
हालांकि, अगर आप बोतल स्रोत (flask.globals) अभी भी नहीं है कि कैसे में देखो अगर request
, g
, current_app
और session
बनाया जाता है। जैसा कि हमने स्थापित किया है, फ्लास्क कई "नकली" अनुरोधों (एक सच्चे HTTP अनुरोध से) को स्पॉन कर सकता है और इस प्रक्रिया में कई एप्लिकेशन फ़्लेट्स को भी धक्का दे सकता है। यह एक सामान्य उपयोग-मामला नहीं है, लेकिन यह ढांचे की क्षमता है। चूंकि ये "समवर्ती" अनुरोध और एप्लिकेशन अभी भी किसी एक समय में केवल "फोकस" होने के साथ चलाने के लिए सीमित हैं, इसलिए यह उनके संबंधित संदर्भ के लिए स्टैक का उपयोग करने के लिए समझ में आता है। जब भी कोई नया अनुरोध किया जाता है या अनुप्रयोगों में से एक को बुलाया जाता है, तो वे अपने संदर्भ को अपने संबंधित स्टैक के शीर्ष पर धकेल देते हैं। फ्लास्क LocalStack
इस उद्देश्य के लिए वस्तुओं का उपयोग करता है । जब वे अपना व्यवसाय समाप्त करते हैं तो वे संदर्भ को स्टैक से बाहर निकालते हैं।
LocalStack
यह वही है जो LocalStack
दिखता है (फिर से तर्क को समझने के लिए कोड को सरल बनाया गया है)।
class LocalStack(object):
def __init__(self):
self.local = Local()
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self.local, 'stack', None)
if rv is None:
self.local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
stack = getattr(self.local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self.local) # this simply releases the local
return stack[-1]
else:
return stack.pop()
@property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self.local.stack[-1]
except (AttributeError, IndexError):
return None
ऊपर से ध्यान दें कि एक LocalStack
स्थानीय में संग्रहीत स्टैक है, न कि स्टैक पर संग्रहीत स्थानीय लोगों का एक समूह। इसका तात्पर्य यह है कि यद्यपि स्टैक विश्व स्तर पर सुलभ है, यह प्रत्येक थ्रेड में एक अलग स्टैक है।
कुप्पी इसकी जरूरत नहीं है request
, current_app
, g
, और session
वस्तुओं एक करने के लिए सीधे हल करने LocalStack
, यह बजाय का उपयोग करता है LocalProxy
वस्तुओं है कि चादर के एक देखने समारोह (बजाय एक की Local
वस्तु) से अंतर्निहित वस्तु मिल जाएगा LocalStack
:
_request_ctx_stack = LocalStack()
def _find_request():
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return top.request
request = LocalProxy(_find_request)
def _find_session():
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return top.session
session = LocalProxy(_find_session)
_app_ctx_stack = LocalStack()
def _find_g():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.g
g = LocalProxy(_find_g)
def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.app
current_app = LocalProxy(_find_app)
इन सभी को एप्लिकेशन स्टार्ट-अप में घोषित किया जाता है, लेकिन वास्तव में किसी भी चीज का तब तक समाधान नहीं होता है जब तक कि अनुरोध संदर्भ या एप्लिकेशन संदर्भ को उनके संबंधित स्टैक में धकेल नहीं दिया जाता है।
यदि आप यह देखने के लिए उत्सुक हैं कि एक संदर्भ वास्तव में स्टैक में कैसे डाला जाता है (और बाद में बाहर निकलता है), तो देखें flask.app.Flask.wsgi_app()
कि wsgi ऐप के प्रवेश का बिंदु क्या है (यानी जब वेब सर्वर कॉल करता है और http वातावरण को पास करता है जब a अनुरोध में आता है), और के निर्माण का पालन RequestContext
सभी इसके बाद के माध्यम से वस्तु push()
में _request_ctx_stack
। एक बार स्टैक के शीर्ष पर धकेल दिया गया, इसके माध्यम से पहुँचा जा सकता है _request_ctx_stack.top
। प्रवाह को प्रदर्शित करने के लिए कुछ संक्षिप्त कोड यहाँ दिए गए हैं:
इसलिए आप एक ऐप शुरू करें और इसे WSGI सर्वर को उपलब्ध कराएं ...
app = Flask(*config, **kwconfig)
# ...
बाद में एक http अनुरोध आता है और WSGI सर्वर सामान्य परमस के साथ ऐप को कॉल करता है ...
app(environ, start_response) # aka app.__call__(environ, start_response)
यह मोटे तौर पर अनुप्रयोग में क्या होता है ...
def Flask(object):
# ...
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response):
ctx = RequestContext(self, environ)
ctx.push()
try:
# process the request here
# raise error if any
# return Response
finally:
ctx.pop()
# ...
और यह मोटे तौर पर RequestContext के साथ होता है ...
class RequestContext(object):
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
self.flashes = None
def push(self):
_request_ctx_stack.push(self)
def pop(self):
_request_ctx_stack.pop()
यह कहें कि एक अनुरोध आरंभ हो गया है, request.path
आपके किसी एक कार्य के लिए लुकअप इस प्रकार होगा:
- विश्व स्तर पर सुलभ
LocalProxy
वस्तु से शुरू करें request
।
- इसकी अंतर्निहित वस्तु को खोजने के लिए (यह जिस वस्तु से जुड़ा हुआ है) इसे अपने लुकअप फंक्शन
_find_request()
(इसे इसके रूप में पंजीकृत फंक्शन self.local
) कहते हैं।
- यह फ़ंक्शन स्टैक पर शीर्ष संदर्भ के लिए
LocalStack
ऑब्जेक्ट _request_ctx_stack
को क्वेरी करता है ।
- शीर्ष संदर्भ खोजने के लिए,
LocalStack
ऑब्जेक्ट पहले उस संपत्ति के लिए अपने आंतरिक Local
गुण ( self.local
) पर सवाल stack
उठाता है जो पहले वहां संग्रहीत किया गया था।
- से
stack
यह शीर्ष संदर्भ हो जाता है
- और
top.request
इस प्रकार ब्याज की अंतर्निहित वस्तु के रूप में हल किया जाता है।
- उस वस्तु से हमें
path
विशेषता मिलती है
इसलिए हमने देखा है कि कैसे Local
, LocalProxy
और LocalStack
काम करते हैं, अब इससे प्राप्त करने के निहितार्थ और बारीकियों के एक पल के लिए सोचें path
:
- एक ऐसी
request
वस्तु जो वैश्विक स्तर पर सुलभ वस्तु होगी।
- एक
request
वस्तु जो स्थानीय होगी।
- किसी
request
स्थानीय की विशेषता के रूप में संग्रहित वस्तु।
- एक
request
वस्तु एक वस्तु एक स्थानीय में संग्रहीत करने के लिए प्रॉक्सी है।
- एक
request
स्टैक पर संग्रहीत एक वस्तु, जो बदले में एक स्थानीय में संग्रहीत होती है।
- एक
request
वस्तु एक ढेर एक स्थानीय में संग्रहीत पर एक वस्तु के लिए एक प्रॉक्सी है। <- यही फ्लास्क करता है।