পূর্ববর্তী উত্তরগুলি ইতিমধ্যে একটি অনুরোধের সময় ফ্লাস্কের পটভূমিতে কী ঘটে যায় তার একটি সুন্দর ওভারভিউ দেয়। আপনি যদি এখনও এটি না পড়ে থাকেন তবে আমি এটি পড়ার আগে @ মার্কহিলড্রেথের উত্তরটি সুপারিশ করছি। সংক্ষেপে, প্রতিটি HTTP অনুরোধের জন্য একটি নতুন প্রসঙ্গ (থ্রেড) তৈরি করা হয়, এজন্য এটি থ্রেড থাকা প্রয়োজনLocal
সুবিধা থাকা প্রয়োজন যা এই জাতীয় বস্তুগুলিকে অনুমতি দেয়request
এবংg
তাদের অনুরোধের নির্দিষ্ট প্রসঙ্গ বজায় রেখে বিশ্বব্যাপী থ্রেডগুলি জুড়ে অ্যাক্সেসযোগ্য হতে হবে। তদ্ব্যতীত, কোনও HTTP অনুরোধ প্রক্রিয়া করার সময় ফ্লাস্ক ভিতরে থেকে অতিরিক্ত অনুরোধগুলি অনুকরণ করতে পারে, সুতরাং তাদের নিজস্ব প্রাসঙ্গিক স্ট্যাকের মধ্যে সঞ্চয় করার প্রয়োজনীয়তা। এছাড়াও, ফ্লাস্ক একাধিক ডাব্লুএসজি অ্যাপ্লিকেশনগুলিকে একক প্রক্রিয়ার মধ্যে একে অপরের সাথে চলার অনুমতি দেয় এবং একটি অনুরোধের সময় একাধিককে ডেকে আনা যেতে পারে (প্রতিটি অনুরোধ একটি নতুন অ্যাপ্লিকেশন প্রসঙ্গ তৈরি করে), সুতরাং অ্যাপ্লিকেশনগুলির জন্য প্রসঙ্গ স্ট্যাকের প্রয়োজন। এটি পূর্বের উত্তরে কী ছিল তা সংক্ষিপ্তসার।
আমার লক্ষ্য হ'ল ফ্ল্যাস্ক এবং ওয়ার্কজেগ কীভাবে এই প্রসঙ্গে স্থানীয়দের সাথে তারা কী করে তা ব্যাখ্যা করে আমাদের বর্তমান বোঝাপড়ার পরিপূরক করা । কোডটির যুক্তি বোঝার জন্য আমি কোডটি সরলীকৃত করেছি, তবে আপনি যদি এটি পান তবে আপনার আসল উত্স ( werkzeug.local
এবং flask.globals
) এর বেশিরভাগ বিষয়গুলি সহজেই উপলব্ধি করতে সক্ষম হওয়া উচিত ।
আসুন প্রথমে বুঝি কীভাবে ওয়ার্কজেগ থ্রেড লোকাল প্রয়োগ করে।
স্থানীয়
যখন কোনও http অনুরোধ আসে, এটি একটি একক থ্রেডের প্রসঙ্গে প্রক্রিয়াকরণ করা হয়। কোনও HTTP অনুরোধ চলাকালীন নতুন প্রসঙ্গ উত্সর্গ করার বিকল্প উপায় হিসাবে, ওয়ার্কজেগ সাধারণ থ্রেডের পরিবর্তে গ্রিনলেটগুলি (এক ধরণের লাইটার "মাইক্রো থ্রেড") ব্যবহারের অনুমতি দেয়। যদি আপনার গ্রিনলেট ইনস্টল না থাকে তবে এটি পরিবর্তে থ্রেড ব্যবহার করে ফিরে যাবে। এই প্রতিটি থ্রেড (বা গ্রিনলেট) একটি অনন্য আইডি দ্বারা সনাক্তযোগ্য, যা আপনি মডিউলটির 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
আগ্রহের অন্য একটি অবজেক্ট (যেমন বস্তুর সাথে এটি প্রক্সি করে) সন্ধান করে qu আসুন আমরা একবার দেখে নিই:
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
, ওয়ার্কজেগ 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()
sertedোকানো হয় তা জানতে আগ্রহী হন (এবং পরবর্তীকালে পপ আউট), দেখুন কীভাবে ডাব্লুএসজি অ্যাপ্লিকেশনটির প্রবেশের বিন্দুটি রয়েছে (যেমন ওয়েব সার্ভারটি কী কল করে এবং HTTP পরিবেশটি কখন পাস করবে যখন অনুরোধ আসে), এবং সৃষ্টির অনুসরণ RequestContext
সব তার পরবর্তী মাধ্যমে বস্তুর push()
মধ্যে _request_ctx_stack
। একবার স্ট্যাকের শীর্ষে ধাক্কা পরে, এটি মাধ্যমে অ্যাক্সেসযোগ্য _request_ctx_stack.top
। প্রবাহটি প্রদর্শনের জন্য কিছু সংক্ষিপ্ত কোড এখানে দেওয়া হয়েছে:
সুতরাং আপনি একটি অ্যাপ্লিকেশন শুরু করুন এবং এটি ডাব্লুএসজিআই সার্ভারে উপলভ্য করুন ...
app = Flask(*config, **kwconfig)
# ...
পরে একটি HTTP অনুরোধ আসে এবং ডাব্লুএসজিআই সার্ভার অ্যাপ্লিকেশনটিকে স্বাভাবিক প্যারাম দিয়ে কল করে ...
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()
# ...
এবং অনুরোধ অনুরোধের সাথে প্রায় এটিই ঘটে ...
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
বস্তুর একটি স্ট্যাক একটি স্থানীয় সঞ্চিত একটি বস্তু করার জন্য কোনো প্রক্সি হয়। <- ফ্লাস্ক এটি করে।