ফ্লাস্কের প্রসঙ্গ স্ট্যাকের উদ্দেশ্য কী?


158

এটি কীভাবে কাজ করে বা কেন এটি কেন ডিজাইন করা হয়েছিল তা পুরোপুরি না বুঝে আমি কিছু সময়ের জন্য অনুরোধ / আবেদনের প্রসঙ্গটি ব্যবহার করছি। অনুরোধ বা অ্যাপ্লিকেশন প্রসঙ্গে আসে যখন "স্ট্যাক" এর উদ্দেশ্য কী? এই দুটি পৃথক স্ট্যাক, না তারা উভয়ই একটি স্ট্যাকের অংশ? অনুরোধের প্রসঙ্গটি কি কোনও স্ট্যাকের উপরে চাপানো হয়েছে, না এটি নিজেই একটি স্ট্যাক? আমি কি প্রত্যেকের উপরে একাধিক প্রসঙ্গ ধাক্কা / পপ করতে সক্ষম? যদি তা হয় তবে আমি কেন এটি করতে চাই?

সমস্ত প্রশ্নের জন্য দুঃখিত, কিন্তু আমি এখনও অনুরোধ প্রসঙ্গে এবং অ্যাপ্লিকেশন প্রসঙ্গে ডকুমেন্টেশন পড়ে বিভ্রান্ত হয়ে পড়েছি।


5
kronosapiens.github.io/blog/2014/08/14/… আইএমও, এই ব্লগ পোস্টটি আমাকে ফ্লাস্ক প্রসঙ্গে সবচেয়ে বোধগম্য বর্ণনা দেয়।
mission.liao

উত্তর:


243

একাধিক অ্যাপস

অ্যাপ্লিকেশন প্রসঙ্গ (এবং এর উদ্দেশ্য) প্রকৃতপক্ষে বিভ্রান্তিকর হয় যতক্ষণ না আপনি বুঝতে পেরেছেন যে ফ্লাস্কের একাধিক অ্যাপ থাকতে পারে। আপনি যেখানে একক ডাব্লুএসজিআই পাইথন ইন্টারপ্রেটার একাধিক ফ্লাস্ক অ্যাপ্লিকেশন চালাতে চান সেই পরিস্থিতিটি কল্পনা করুন। আমরা এখানে ব্লুপ্রিন্টের কথা বলছি না, আমরা সম্পূর্ণ বিভিন্ন ফ্লাস্ক অ্যাপ্লিকেশনগুলির সাথে কথা বলছি।

আপনি এটি "অ্যাপ্লিকেশন প্রেরণকারী" উদাহরণে ফ্লাস্ক ডকুমেন্টেশন বিভাগের অনুরূপ সেট আপ করতে পারেন :

from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend':     backend
})

লক্ষ্য করুন যে দুটি সম্পূর্ণ আলাদা ফ্লাস্ক অ্যাপ্লিকেশন তৈরি হচ্ছে "ফ্রন্টএন্ড" এবং "ব্যাকএন্ড" being অন্য কথায়, Flask(...)অ্যাপ্লিকেশন কনস্ট্রাক্টরকে দু'বার কল করা হয়েছে, যা ফ্লাস্ক অ্যাপ্লিকেশনটির দুটি উদাহরণ তৈরি করেছে।

অনুষঙ্গ

আপনি যখন ফ্লাস্কের সাথে কাজ করছেন, আপনি প্রায়শই বিভিন্ন কার্যকারিতা অ্যাক্সেস করতে গ্লোবাল ভেরিয়েবল ব্যবহার করে শেষ করেন। উদাহরণস্বরূপ, আপনার কাছে সম্ভবত এমন কোড রয়েছে যা পড়ে ...

from flask import request

তারপরে, একটি দৃশ্যের সময় আপনি requestবর্তমান অনুরোধের তথ্য অ্যাক্সেস করতে ব্যবহার করতে পারেন। স্পষ্টতই, requestকোনও সাধারণ বৈশ্বিক পরিবর্তনশীল নয়; বাস্তবে, এটি একটি প্রসঙ্গ স্থানীয় মান। অন্য কথায়, সেখানে লোকচক্ষুর বলে পিছনে কিছু জাদু "যখন আমি ফোন request.pathপেতে pathথেকে অ্যাট্রিবিউট requestবর্তমান অনুরোধের বস্তুর।" দুটি ভিন্ন অনুরোধের জন্য একটি ভিন্ন ফলাফল হবে request.path

আসলে, আপনি একাধিক থ্রেড দিয়ে ফ্লাস্ক চালালেও, অনুরোধের বিষয়গুলি বিচ্ছিন্ন রাখতে ফ্লাস্ক যথেষ্ট স্মার্ট। এটি করার জন্য, দুটি থ্রেডের পক্ষে, প্রতিটি আলাদা আলাদা অনুরোধ পরিচালনা করে, একই সাথে কল করা request.pathএবং তাদের নিজ নিজ অনুরোধের জন্য সঠিক তথ্য পাওয়া সম্ভব হয়ে ওঠে ।

একসাথে রেখে

সুতরাং আমরা ইতিমধ্যে দেখেছি যে ফ্লাস্ক একই দোভাষীর একাধিক অ্যাপ্লিকেশন পরিচালনা করতে পারে এবং যে কারণে "ফ্ল্যাট" আপনাকে "প্রসঙ্গ স্থানীয়" গ্লোবাল ব্যবহার করতে দেয় সেখানে "বর্তমান" অনুরোধটি কী তা নির্ধারণ করার জন্য কিছু ব্যবস্থা থাকতে হবে ( যাতে কিছু করার জন্য request.path)।

এই ধারণাগুলি একত্রে রাখলে, এও বোঝা উচিত যে "বর্তমান" অ্যাপ্লিকেশনটি কী তা নির্ধারণ করার জন্য ফ্লাস্কের অবশ্যই কিছু উপায় থাকতে হবে!

আপনারও সম্ভবত নীচের মতো কোড রয়েছে:

from flask import url_for

আমাদের requestউদাহরণের মতো , url_forফাংশনে যুক্তি রয়েছে যা বর্তমান পরিবেশের উপর নির্ভরশীল। তবে এক্ষেত্রে এটি দেখতে স্পষ্ট যে যুক্তিটি প্রচলিত নির্ভর করে কোন অ্যাপ্লিকেশনটিকে "বর্তমান" অ্যাপ্লিকেশন হিসাবে বিবেচনা করা হয় তার উপর। উপরের দেখানো ফ্রন্টএন্ড / ব্যাকএন্ডের উদাহরণে, "ফ্রন্টএন্ড" এবং "ব্যাকএন্ড" অ্যাপ্লিকেশনগুলির মধ্যে একটি "/ লগইন" রুট থাকতে পারে এবং তাই url_for('/login')ভিউটি ফ্রন্টএন্ড বা ব্যাকএন্ড অ্যাপের জন্য অনুরোধটি পরিচালনা করছে কিনা তার উপর নির্ভর করে কিছু অন্যরকম ফিরে আসতে হবে return

আপনার প্রশ্নের উত্তর দিতে ...

অনুরোধ বা অ্যাপ্লিকেশন প্রসঙ্গে আসে যখন "স্ট্যাক" এর উদ্দেশ্য কী?

অনুরোধ প্রসঙ্গে ডক্স থেকে:

কারণ অনুরোধের প্রসঙ্গটি অভ্যন্তরীণভাবে একটি স্ট্যাক হিসাবে বজায় রাখা হয়েছে আপনি একাধিকবার ধাক্কা এবং পপ করতে পারেন। অভ্যন্তরীণ পুনঃনির্দেশগুলির মতো জিনিসগুলি প্রয়োগ করতে এটি খুব সহজ।

অন্য কথায়, যদিও আপনার কাছে "বর্তমান" অনুরোধ বা "বর্তমান" অ্যাপ্লিকেশনগুলির এই স্ট্যাকটিতে সাধারণত 0 বা 1 টি আইটেম থাকবে তবে সম্ভবত আপনার আরও কিছু থাকতে পারে।

প্রদত্ত উদাহরণটি হ'ল যেখানে আপনার অনুরোধটি "অভ্যন্তরীণ পুনর্নির্দেশ" এর ফলাফল ফিরিয়ে দেবে। যাক কোনও ব্যবহারকারী এ কে অনুরোধ করে তবে আপনি ব্যবহারকারী বিতে ফিরে যেতে চান বেশিরভাগ ক্ষেত্রে আপনি ব্যবহারকারীর কাছে পুনর্নির্দেশ ইস্যু করেন এবং ব্যবহারকারীকে বি রিসোর্সের দিকে নির্দেশ করেন, যার অর্থ ব্যবহারকারী বি বি আনার জন্য দ্বিতীয় অনুরোধ চালাবেন A এটি হ্যান্ডেল করার সামান্য ভিন্ন উপায় হবে অভ্যন্তরীণ পুনর্নির্দেশ করা, যার অর্থ এ এর ​​প্রক্রিয়া চলাকালীন, ফ্লাস্ক রিসোর্স বিয়ের জন্য নিজের কাছে একটি নতুন অনুরোধ জানাবে এবং ব্যবহারকারীর আসল অনুরোধের ফলাফল হিসাবে এই দ্বিতীয় অনুরোধের ফলাফলগুলি ব্যবহার করবে।

এই দুটি পৃথক স্ট্যাক, না তারা উভয়ই একটি স্ট্যাকের অংশ?

তারা দুটি পৃথক স্ট্যাক । তবে এটি বাস্তবায়নের বিশদ। এর চেয়ে গুরুত্বপূর্ণ বিষয়টি এতটা নয় যে কোনও স্ট্যাক রয়েছে, তবে যে কোনও সময় আপনি "বর্তমান" অ্যাপ্লিকেশন বা অনুরোধ পেতে পারেন (স্ট্যাকের শীর্ষস্থানীয়)।

অনুরোধের প্রসঙ্গটি কি কোনও স্ট্যাকের উপরে চাপানো হয়েছে, না এটি নিজেই একটি স্ট্যাক?

একটি "অনুরোধ প্রসঙ্গ" "অনুরোধ প্রসঙ্গ স্ট্যাক" এর একটি আইটেম। একইভাবে "অ্যাপ্লিকেশন প্রসঙ্গ" এবং "অ্যাপ্লিকেশন প্রসঙ্গ স্ট্যাক" দিয়ে।

আমি কি প্রত্যেকের উপরে একাধিক প্রসঙ্গ ধাক্কা / পপ করতে সক্ষম? যদি তা হয় তবে আমি কেন এটি করতে চাই?

ফ্লাস্ক অ্যাপ্লিকেশনটিতে আপনি সাধারণত এটি করবেন না। আপনি যেখানে চাইতে পারেন তার একটি উদাহরণ অভ্যন্তরীণ পুনর্নির্দেশের জন্য (উপরে বর্ণিত)। এমনকি সেক্ষেত্রে, সম্ভবত আপনি ফ্লাস্ককে একটি নতুন অনুরোধ হ্যান্ডেল করে দেবেন, এবং তাই ফ্লাস্ক আপনার জন্য সমস্ত ধাক্কা / পপিং করবে do

যাইহোক, কিছু ক্ষেত্রে রয়েছে যেখানে আপনি নিজেরাই স্ট্যাকটি পরিচালনা করতে চান।

একটি অনুরোধের বাইরে চলমান কোড

লোকেদের একটি সাধারণ সমস্যা হ'ল তারা ফ্ল্যাস্ক-এসকিউএএএলএলএকএলচিই এক্সটেনশানটি কোড ব্যবহার করে একটি এসকিউএল ডাটাবেস সেট করতে এবং মডেল সংজ্ঞাটি নীচে দেখানো মত কিছু ব্যবহার করে ...

app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)

তারপরে তারা কোনও স্ক্রিপ্টে appএবং dbমানগুলি ব্যবহার করে যা শেল থেকে চালানো উচিত। উদাহরণস্বরূপ, একটি "setup_tables.py" স্ক্রিপ্ট ...

from myapp import app, db

# Set up models
db.create_all()

এই ক্ষেত্রে, ফ্লাস্ক-এসকিউএএলএলএকএলএমই এক্সটেনশনটি appঅ্যাপ্লিকেশন সম্পর্কে জানে , তবে create_all()এটির সময় অ্যাপ্লিকেশন প্রসঙ্গটি না থাকার বিষয়ে অভিযোগ করার সময় ত্রুটি ঘটবে। এই ত্রুটি ন্যায়সঙ্গত; create_allপদ্ধতি চালানোর সময় কোন অ্যাপ্লিকেশনটির সাথে আচরণ করা উচিত তা আপনি কখনই ফ্লস্ককে জানাননি ।

আপনি হয়ত ভাবছেন যে আপনি with app.app_context()যখন নিজের মতামতগুলিতে একই রকম ফাংশন পরিচালনা করেন তখন কেন আপনার এই কলটির প্রয়োজন হয় না । কারণ হ'ল প্রকৃত ওয়েব অনুরোধগুলি পরিচালনা করার সময় ফ্ল্যাশ ইতিমধ্যে আপনার জন্য অ্যাপ্লিকেশন প্রসঙ্গটির পরিচালনা পরিচালনা করে। সমস্যাটি কেবলমাত্র এই দর্শন ফাংশনগুলির (বা এই জাতীয় কলব্যাকগুলির) বাইরে যেমন আপনার মডেলগুলি যখন এক-অফ স্ক্রিপ্টে ব্যবহার করার সময় আসে তখনই আসে।

রেজোলিউশনটি হ'ল অ্যাপ্লিকেশন প্রসঙ্গে নিজেই চাপ দিন, যা করে কাজটি করা যায় ...

from myapp import app, db

# Set up models
with app.app_context():
    db.create_all()

এটি একটি নতুন অ্যাপ্লিকেশন প্রসঙ্গে ধাক্কা দেবে (অ্যাপ্লিকেশন ব্যবহার করে appমনে রাখবেন যে একাধিক অ্যাপ্লিকেশন থাকতে পারে)।

পরীক্ষামূলক

আরেকটি ক্ষেত্রে যেখানে আপনি স্ট্যাকটি পরিচালনা করতে চান তা পরীক্ষার জন্য। আপনি একটি ইউনিট পরীক্ষা তৈরি করতে পারেন যা একটি অনুরোধ পরিচালনা করে এবং আপনি ফলাফলগুলি পরীক্ষা করেন:

import unittest
from flask import request

class MyTest(unittest.TestCase):
    def test_thing(self):
        with app.test_request_context('/?next=http://example.com/') as ctx:
            # You can now view attributes on request context stack by using `request`.

        # Now the request context stack is empty

3
এটি এখনও আমার কাছে বিভ্রান্ত! কেন একটি একক অনুরোধ প্রসঙ্গ নেই এবং যদি আপনি অভ্যন্তরীণ পুনর্নির্দেশ করতে চান তবে এটি প্রতিস্থাপন করুন। আমার কাছে একটি পরিষ্কার ডিজাইনের মতো মনে হচ্ছে।
মার্টেন

@ ম্যার্টেন যদি অনুরোধ হ্যান্ডল করার সময় আপনি অনুরোধ বি করেন, এবং অনুরোধ বি স্ট্যাকের অনুরোধ এটির পরিবর্তে, অনুরোধের জন্য হ্যান্ডলিং শেষ করতে পারে না। তবে, আপনি প্রস্তাবিত প্রতিস্থাপনের কৌশলটি স্ট্যাক না করেও এবং স্ট্যাক না থাকলেও (অভ্যন্তরীণ পুনঃনির্দেশগুলি আরও কঠিন হবে) এটি অ্যাপ্লিকেশন এবং অনুরোধের প্রসঙ্গে অনুরোধগুলি পরিচালনা করা আলাদা করার জন্য সত্যটি সত্য পরিবর্তন করে না।
মার্ক হিলড্রেথ

ভাল ব্যাখ্যা! তবে আমি এখনও কিছুটা বিভ্রান্তির মধ্যে রয়েছি: "অ্যাপ্লিকেশন প্রসঙ্গটি প্রয়োজনীয় হিসাবে তৈরি এবং ধ্বংস করা হয় It এটি কখনও থ্রেডের মধ্যে চলে না এবং এটি অনুরোধগুলির মধ্যে ভাগ করা হবে না will" ফ্লাস্কের নথিতে অ্যাপ্লিকেশনটির সাথে কেন একটি "অ্যাপ্লিকেশন প্রসঙ্গ" বজায় থাকে না?
জাভেন

1
ফ্লাস্কের অভ্যন্তরীণ পুনর্নির্দেশের উদাহরণ সহায়ক হবে, গুগল করা এটি খুব বেশি পরিণত হয় না। যদি না হয় তবে request = Local()গ্লোবাল.পাইয়ের জন্য একটি সহজ নকশা যথেষ্ট নয়? এমন ব্যবহারের ক্ষেত্রে সম্ভবত আমি ভাবছি না are
চতুর্থাংশ

ভিউগুলি আমদানি করার সময় ফ্যাক্টরি পদ্ধতির অভ্যন্তরে অ্যাপের প্রসঙ্গটি চাপানো কি ঠিক? কারন ভিউগুলিতে কারেন্ট_এপকে নির্দেশ করে এমন রুট রয়েছে যা আমার প্রসঙ্গে দরকার।
পরিবর্তনশীল

48

পূর্ববর্তী উত্তরগুলি ইতিমধ্যে একটি অনুরোধের সময় ফ্লাস্কের পটভূমিতে কী ঘটে যায় তার একটি সুন্দর ওভারভিউ দেয়। আপনি যদি এখনও এটি না পড়ে থাকেন তবে আমি এটি পড়ার আগে @ মার্কহিলড্রেথের উত্তরটি সুপারিশ করছি। সংক্ষেপে, প্রতিটি 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বস্তুর একটি স্ট্যাক একটি স্থানীয় সঞ্চিত একটি বস্তু করার জন্য কোনো প্রক্সি হয়। <- ফ্লাস্ক এটি করে।

4
দুর্দান্ত রুনডাউন, আমি ফ্লাস্ক / গ্লোবাল.পি এবং ওয়ার্কজেগ / লোকাল.পি তে কোডটি অধ্যয়ন করছি এবং এটি আমার এটি বোঝার বিষয়টি পরিষ্কার করতে সহায়তা করে। আমার স্পাইডি ইন্দ্রিয়টি আমাকে বলে যে এটি একটি উপায়ে জটিল নকশা, তবে আমি স্বীকার করি যে এটির জন্য ব্যবহৃত সমস্ত ব্যবহারের ক্ষেত্রে আমি বুঝতে পারি না। "অভ্যন্তরীণ পুনঃনির্দেশ" একমাত্র যুক্তি যা আমি উপরের বর্ণনায় দেখেছি এবং "ফ্লেস্ক অভ্যন্তরীণ পুনর্নির্দেশ" গুগল করা খুব বেশি আপ হয় না তাই আমি এখনও কিছুটা ক্ষতিতে আছি। ফ্লাস্ক সম্পর্কে আমার পছন্দের জিনিসগুলির মধ্যে একটি হ'ল এটি সাধারণত জাভা অবজেক্ট-স্যুপ টাইপ জিনিসটি অ্যাবস্ট্রাকপ্রভাইডারকন্টেক্সটবেস ফ্যাক্টরিগুলি এবং এর মতো পূর্ণ নয়।
চতুর্থাংশ

1
@QuadrupleA একবার আপনি বুঝতে কিভাবে এই Local, LocalStackএবং LocalProxyকাজ, আমি ডক এই নিবন্ধ পরিদর্শন সুপারিশ: flask.pocoo.org/docs/0.11/appcontext , flask.pocoo.org/docs/0.11/extensiondev এবং flask.pocoo .org / ডক্স / 0.11 / রিকনটেক্সট । আপনার তাজা উপলব্ধি আপনাকে এগুলিকে একটি নতুন আলো দেখায় এবং আরও অন্তর্দৃষ্টি প্রদান করতে পারে।
মাইকেল একোকা

এই লিঙ্কগুলির মাধ্যমে পড়ুন - এগুলি বেশিরভাগই বোধগম্য হয় তবে ডিজাইনটি এখনও আমাকে অত্যধিক জটিল এবং তার নিজের ভালোর জন্য খুব চালাক হিসাবে আঘাত করে। তবে আমি সাধারণভাবে ওওপি-র বড় অনুরাগী না এবং অন্তর্নিহিত প্রবাহ নিয়ন্ত্রণ উপাদান (ওভাররাইডিং __call __ (), __getattr __ (), গতিশীল ইভেন্ট প্রেরণ বনাম সাধারণ ফাংশন কলগুলি, সম্পত্তি নিয়ামকগুলিতে জিনিসগুলিকে মোড়ানো কেবল নিয়মিত বৈশিষ্ট্য ব্যবহার না করে ইত্যাদি etc ।) সুতরাং এটি দর্শনের মধ্যে কেবল একটি পার্থক্য। কোনও টিডিডি অনুশীলনকারীও নয়, যা এই অতিরিক্ত যন্ত্রপাতিটিকে অনেকগুলি সমর্থন করার উদ্দেশ্যে বলে মনে হয়।
চতুর্থাংশ

1
এই ভাগ করে নেওয়ার জন্য ধন্যবাদ, প্রশংসা। পাইথনের মতো ভাষার সাথে থ্রেডিং হ'ল দুর্বলতা - আপনি অ্যাপ্লিকেশন ফ্রেমওয়ার্কগুলিতে le উপরের মতো ফুটোটির মতো নিদর্শনগুলি দিয়ে সজ্জিত হন এবং যা সত্যই কোনও আকার দেয় না। জাভা একই পরিস্থিতি পুনরায় অন্য একটি উদাহরণ। থ্রেডলোকালস, সেম্যাফোর্স ইত্যাদি। ডান পেতে, বা বজায় রাখা কুখ্যাত কাজ। এখানেই এরলং / এলিক্সির মতো ভাষা (বিএএম ব্যবহার করে), বা ইভেন্ট লুপের পদ্ধতির (যেমন এনগিনেক্স বনাম অ্যাপাচি ইত্যাদি) সাধারণত আরও শক্তিশালী, স্কেলেবল এবং কম জটিল পদ্ধতির প্রস্তাব দেয়।
আর্সেল্ডন

13

সামান্য সংযোজন @ মার্ক হিলড্রেথ উত্তর।

প্রসঙ্গ স্ট্যাকের মতো দেখতে {thread.get_ident(): []}, যেখানে []"স্ট্যাক" বলা হয় কারণ এটি শুধুমাত্র append( push) popএবং [-1]( __getitem__(-1)) ক্রিয়াকলাপ ব্যবহৃত হয়। সুতরাং প্রসঙ্গ স্ট্যাক থ্রেড বা গ্রিনলেট থ্রেডের জন্য প্রকৃত ডেটা রাখবে।

current_app, g, request, sessionএবং ইত্যাদি হয় LocalProxyবস্তুর শুধু বিশেষ পদ্ধতি overrided __getattr__, __getitem__, __call__, __eq__এবং ইত্যাদি এবং প্রসঙ্গ স্ট্যাকের শীর্ষ (থেকে ফেরত মান [-1]) যুক্তি নামে ( current_app, requestউদাহরণস্বরূপ)। LocalProxyএই বস্তুগুলি একবার আমদানি করতে হবে এবং তারা বাস্তবতা মিস করবে না। আপনি কেবল requestকোডে থাকুন না কেন কেবল আমদানি করুন তার পরিবর্তে আপনার কার্য এবং পদ্ধতিতে অনুরোধ যুক্তি প্রেরণ করে খেলুন। আপনি এটির সাথে সহজেই নিজের এক্সটেনশানগুলি লিখতে পারেন তবে ভুলে যাবেন না যে অবাস্তব ব্যবহার কোড বোঝার পক্ষে আরও কঠিন করে তুলতে পারে।

Https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/local.py বুঝতে সময় ব্যয় করুন ।

সুতরাং কিভাবে উভয় স্ট্যাক পপুলেশন? অনুরোধে Flask:

  1. request_contextপরিবেশ দ্বারা তৈরি (init map_adapter, ম্যাচের পথ)
  2. এই অনুরোধটি প্রবেশ বা চাপ দিন:
    1. পূর্ববর্তী পরিষ্কার request_context
    2. app_contextযদি এটি মিস হয়ে যায় এবং অ্যাপ্লিকেশন প্রসঙ্গ স্ট্যাকের দিকে ঠেলা দেয় তবে এটি তৈরি করুন
    3. এই অনুরোধ প্রসঙ্গ স্ট্যাক অনুরোধ চাপানো
    4. এটি অনুপস্থিত হলে init অধিবেশন
  3. প্রেরণের অনুরোধ
  4. পরিষ্কার অনুরোধ এবং স্ট্যাক থেকে পপ

2

একটি উদাহরণ নেওয়া যাক, ধরুন আপনি একটি ইউজারকেক্সট সেট করতে চান (লোকাল এবং লোকালপ্রক্সির ফ্লাস্ক কনস্ট্রাক্ট ব্যবহার করে)।

একটি ব্যবহারকারীর শ্রেণি নির্ধারণ করুন:

class User(object):
    def __init__(self):
        self.userid = None

বর্তমান থ্রেড বা গ্রিনলেটের ভিতরে ব্যবহারকারী অবজেক্টটি পুনরুদ্ধার করতে একটি ফাংশন সংজ্ঞায়িত করুন

def get_user(_local):
    try:
        # get user object in current thread or greenlet
        return _local.user
    except AttributeError:
        # if user object is not set in current thread ,set empty user object 
       _local.user = User()
    return _local.user

এখন একটি লোকালপ্রক্সি সংজ্ঞায়িত করুন

usercontext = LocalProxy(partial(get_user, Local()))

এখনকার থ্রেডে ইউজারের ব্যবহারকারী পেতে

ব্যাখ্যা:

1. লোকালের পরিচয় এবং উদ্দেশ্য সম্পর্কিত একটি আদেশ রয়েছে, পরিচয়টি থ্রেডিড বা গ্রিনলেট আইডি, উদাহরণস্বরূপ _local.user = ব্যবহারকারী () _local এর সমপরিমাণ ___ সঞ্চয়স্থান __ [বর্তমান থ্রেডের আইডি] ["ব্যবহারকারী"] = ব্যবহারকারী ()

  1. লোকালপ্রক্সি স্থানীয় অবজেক্ট মোড়ানোর জন্য অপারেশনকে প্রতিনিধিত্ব করে অথবা আপনি এমন কোনও ক্রিয়াকলাপ সরবরাহ করতে পারেন যা টার্গেট অবজেক্টটিকে ফেরত দেয়। উপরের উদাহরণে get_user ফাংশন লোকালপ্রক্সিতে বর্তমান ব্যবহারকারীর অবজেক্ট সরবরাহ করে এবং আপনি যখন ব্যবহারকারী কনটেক্সট.উসারিড দ্বারা বর্তমান ব্যবহারকারীর ইউজারিডের জন্য জিজ্ঞাসা করেন, লোকালপ্রক্সির __getattr__ ফাংশনটি প্রথমে get_user কল করে ব্যবহারকারী অবজেক্ট (ব্যবহারকারী) এবং তারপরে getattr (ইউজার, "ইউজারিড") কল করে। ব্যবহারকারীর উপর ইউজারিড সেট করতে (বর্তমান থ্রেড বা গ্রিনলেটে) আপনি সহজভাবে যা করতে পারেন: usercontext.userid = "user_123"
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.