বিদ্যমান তথ্য সংরক্ষণ করে একটি ভিন্ন ধরণের এবং বার্তা সহ পুনরায় উত্থাপন ব্যতিক্রম


139

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

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

আমার মডিউলের fooনির্দিষ্ট ব্যতিক্রমগুলি বিদ্যমান প্রকারের (যেমন class FooPermissionError(OSError, FooError)) থেকে নির্দিষ্ট করে এটিকে আংশিকভাবে সমাধান করা হয়েছে , তবে এটি বিদ্যমান ব্যতিক্রম উদাহরণটি কোনও নতুন ধরণে लपेटতে বা বার্তা পরিবর্তন করতে কোনও সহজ করে না।

পাইথনের পিইপি 3134 "এক্সপেশন চেইনিং অ্যান্ড এমবেডেড ট্রেসব্যাকস" পাইথন 3.0 এ "চেইনিং" ব্যতিক্রম আইটেমের জন্য গৃহীত একটি পরিবর্তন নিয়ে আলোচনা করে, এটি প্রমাণ করতে যে একটি বিদ্যমান ব্যতিক্রম পরিচালনা করার সময় একটি নতুন ব্যতিক্রম উত্থাপিত হয়েছিল।

আমি যা করার চেষ্টা করছি তা সম্পর্কিত: আমার এটি পাইথন সংস্করণের আগের সংস্করণেও কাজ করা প্রয়োজন এবং আমার এটি শৃঙ্খলার জন্য নয়, কেবল পলিমারফিজমের জন্য প্রয়োজন। এটি করার সঠিক উপায় কী?


ব্যতিক্রমগুলি ইতিমধ্যে সম্পূর্ণ পলিমারফিক - এগুলি ব্যতিক্রমের সমস্ত সাবক্লাস। আপনি কি করতে চেষ্টা করছেন? শীর্ষস্থানীয় ব্যতিক্রম হ্যান্ডলারের সাথে "ভিন্ন বার্তা" মোটামুটি তুচ্ছ। আপনি ক্লাস পরিবর্তন করছেন কেন?
এসলট

প্রশ্নে ব্যাখ্যা করা হয়েছে (এখন, আপনার মন্তব্যের জন্য ধন্যবাদ): আমি যে ব্যতিক্রম ধরা পড়েছি তা সাজাতে চেষ্টা করছি, যাতে এটি আরও তথ্য দিয়ে আরও প্রচার করতে পারে তবে কোনও হারায় না not একটি শীর্ষ স্তরের হ্যান্ডলার খুব দেরিতে।
বিগনোজ করুন

দয়া করে আমার CausedException ক্লাসটি দেখুন যা পাইথন ২.x এ আপনি যা করতে পারেন তা করতে পারেন। পাইথন 3-এও যদি আপনি আপনার ব্যতিক্রমের কারণ হিসাবে একাধিক মূল ব্যতিক্রম দিতে চান তবে এটি কার্যকর হতে পারে। হতে পারে এটি আপনার প্রয়োজন অনুসারে ফিট করে।
Alfe


পাইথন -২ এর জন্য আমি @ ডেভিজনজিনপিয়েরের অনুরূপ কিছু করি তবে আমি কেবল একটি নতুন স্ট্রিং বার্তা যুক্ত করছি: except Exception as e-> raise type(e), type(e)(e.message + custom_message), sys.exc_info()[2]-> এই সমাধানটি অন্য একটি প্রশ্ন থেকে । এটি সুন্দর নয় তবে কার্যকরী।
ট্রেভর বয়েড স্মিথ

উত্তর:


197

পাইথন 3 ব্যতিক্রমী শৃঙ্খলা প্রবর্তন করে ( পিইপি 3134 তে বর্ণিত হিসাবে )। এটি একটি ব্যতিক্রম উত্থাপন করার সময় একটি বিদ্যমান ব্যতিক্রমকে "কারণ" হিসাবে উল্লেখ করার অনুমতি দেয়:

try:
    frobnicate()
except KeyError as exc:
    raise ValueError("Bad grape") from exc

ধরা পড়া ব্যতিক্রম ততক্ষণে নতুন ব্যতিক্রম ("কারণ") এর অংশ হয়ে যায় এবং কোডটি যে নতুন ব্যতিক্রমটি ধারণ করে তা উপলভ্য।

এই বৈশিষ্ট্যটি ব্যবহার করে, __cause__বৈশিষ্ট্যটি সেট করা আছে। অন্তর্নির্মিত ব্যতিক্রম হ্যান্ডলার এছাড়াও ট্রেসব্যাকের পাশাপাশি কীভাবে ব্যতিক্রমটির "কারণ" এবং "প্রসঙ্গ" প্রতিবেদন করবেন তাও জানেন


ইন পাইথন 2 , মনে হচ্ছে এই ব্যবহারের ক্ষেত্রে কোন ভাল উত্তর (যেমন বর্ণনা করেছেন ইয়ান Bicking এবং নেদ Batchelder )। হতাশাজনক।


4
আয়ান বাইকিং কি আমার সমাধান বর্ণনা করে না? আমি আফসোস করছি যে আমি এরকম ভ্রষ্ট জবাব দিয়েছি, কিন্তু এটি অদ্ভুত যে এইটি গৃহীত হয়েছিল।
ডেভিন জিনপিয়ের

1
@ বিগনোজ আপনি আমার বক্তব্যটি কেবল সঠিক থেকে নয়, তবে "ফ্রোবনেট" ব্যবহারের জন্যও পেয়েছেন :)
ডেভিড এম

5
ব্যতিক্রম শৃঙ্খলা আসলে এখন ডিফল্ট আচরণ, প্রকৃতপক্ষে এটি সমস্যার বিপরীত, কাজটির জন্য প্রথম ব্যতিক্রমটিকে দমন করে দেখুন, পিইপি 409 পাইথন.আর.দেব
পেপস

1
অজগর 2 এ আপনি কীভাবে এটি সম্পাদন করবেন?
সেলোটেপ

1
দেখে মনে হচ্ছে এটি ঠিক আছে (অজগর ২.7)try: return 2 / 0 except ZeroDivisionError as e: raise ValueError(e)
অ্যালেক্স

37

আপনি ট্রেসব্যাক পেতে sys.exc_info () ব্যবহার করতে পারেন এবং বলেছেন ট্রেসব্যাক (পিইপি উল্লেখ হিসাবে) দিয়ে আপনার নতুন ব্যতিক্রম বাড়াতে পারেন। আপনি যদি পুরানো প্রকার এবং বার্তা সংরক্ষণ করতে চান তবে আপনি ব্যাতিক্রম করতে পারেন তবে এটি কেবলমাত্র কার্যকর যদি আপনার ব্যতিক্রম যা ধরা পড়ে তা সন্ধান করে।

উদাহরণ স্বরূপ

import sys

def failure():
    try: 1/0
    except ZeroDivisionError, e:
        type, value, traceback = sys.exc_info()
        raise ValueError, ("You did something wrong!", type, value), traceback

অবশ্যই, এটি সত্যই কার্যকর নয়। যদি এটি হত তবে আমাদের সেই পিইপি দরকার হবে না। আমি এটি করার সুপারিশ করব না।


ডিভিন, আপনি সেখানে ট্রেসব্যাকের একটি রেফারেন্স সঞ্চয় করেন, আপনি কি সেই উল্লেখটি স্পষ্টভাবে মুছবেন না?
আরাফ্যাজিওন

2
আমি কিছু সঞ্চয় করিনি, আমি স্থানীয় ভেরিয়েবল হিসাবে ট্রেসব্যাক রেখেছি যা সম্ভবত সম্ভাবনার বাইরে চলে যায়। হ্যাঁ, এটি অনুমেয় যে এটি তা নয়, তবে আপনি যদি বিশ্বব্যাপী কর্মক্ষেত্রে না হয়ে এই জাতীয় ব্যতিক্রমগুলি উত্থাপন করেন তবে আপনার বড় সমস্যা রয়েছে। যদি আপনার অভিযোগটি কেবলমাত্র এটি বৈশ্বিক পরিধিতে কার্যকর করা যায় তবে সঠিক সমাধানটি অপ্রাসঙ্গিক বয়লারপ্লেট যুক্ত করা নয় যা 99% ব্যবহারের জন্য প্রাসঙ্গিকভাবে ব্যাখ্যা করা উচিত এবং প্রাসঙ্গিক নয়, তবে সমাধানটি আবারও লিখতে হবে যাতে এ জাতীয় কোনও বিষয় নেই এটি তৈরি করার সময় মনে হয় যেন কিছুই আলাদা নয় - যেমনটি আমি এখন করেছি।
ডেভিন জিনপিয়ের 15

4
আরাফ্যাজিওন , @ ডেভিনের জন্য পাইথন ডকুমেন্টেশনেsys.exc_info() একটি সতর্কতার কথা উল্লেখ করছে। এটি বলে, "কোনও ব্যতিক্রম পরিচালনা করছে এমন কোনও ফাংশনে স্থানীয় ভেরিয়েবলকে ট্রেসব্যাক রিটার্ন মান নির্ধারণের ফলে একটি বিজ্ঞপ্তি রেফারেন্স ঘটবে।" তবে, নিম্নলিখিত নোটটিতে বলা হয়েছে যে পাইথন ২.২ থেকে চক্রটি পরিষ্কার করা যেতে পারে তবে এটি এড়াতে আরও দক্ষ।
ডন কার্কবি

5
: দুই আলোকিত pythonistas থেকে পাইথন মধ্যে পুনরায় বাড়াতে ব্যতিক্রম বিভিন্ন উপায় আরো বিস্তারিত ইয়ান Bicking এবং নেদ Batchelder
রড্রিগো

11

আপনি আপনার নিজস্ব ব্যতিক্রম প্রকার তৈরি করতে পারেন যা আপনি যে কোনও ব্যতিক্রমকে ধরে রেখেছেন extend

class NewException(CaughtException):
    def __init__(self, caught):
        self.caught = caught

try:
    ...
except CaughtException as e:
    ...
    raise NewException(e)

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

সম্পাদনা: আপনার নিজের ব্যতিক্রম ছুঁড়ে ফেলার এবং আসল ব্যতিক্রম রাখার উপায়গুলির এই বিশ্লেষণটি আমি খুঁজে পেয়েছি । কোনও সুন্দর সমাধান নেই।


1
আমি যে ব্যবহারের ক্ষেত্রে বর্ণনা করেছি তা ব্যতিক্রম পরিচালনার জন্য নয় ; এটি বিশেষত এটি পরিচালনা না করার বিষয়ে , তবে কিছু অতিরিক্ত তথ্য (একটি অতিরিক্ত শ্রেণি এবং একটি নতুন বার্তা) যুক্ত করা যাতে এটি কল স্ট্যাকটি আরও সামলাতে পারে।
বিগনোজ করুন

2

আমি আরও দেখতে পেয়েছি যে অনেক সময় ত্রুটি উত্থাপিত হওয়ার জন্য আমার কিছু "মোড়ানো" দরকার।

এটি একটি ফাংশন স্কোপ উভয়ই অন্তর্ভুক্ত এবং কখনও কখনও একটি ফাংশন ভিতরে শুধুমাত্র কিছু লাইন মোড়ানো।

একটি decoratorএবং ব্যবহার করার জন্য একটি মোড়ক তৈরি করেছেন context manager:


বাস্তবায়ন

import inspect
from contextlib import contextmanager, ContextDecorator
import functools    

class wrap_exceptions(ContextDecorator):
    def __init__(self, wrapper_exc, *wrapped_exc):
        self.wrapper_exc = wrapper_exc
        self.wrapped_exc = wrapped_exc

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not exc_type:
            return
        try:
            raise exc_val
        except self.wrapped_exc:
            raise self.wrapper_exc from exc_val

    def __gen_wrapper(self, f, *args, **kwargs):
        with self:
            for res in f(*args, **kwargs):
                yield res

    def __call__(self, f):
        @functools.wraps(f)
        def wrapper(*args, **kw):
            with self:
                if inspect.isgeneratorfunction(f):
                    return self.__gen_wrapper(f, *args, **kw)
                else:
                    return f(*args, **kw)
        return wrapper

ব্যবহারের উদাহরণ

প্রসাধক

@wrap_exceptions(MyError, IndexError)
def do():
   pass

doপদ্ধতি কল করার সময় IndexError, ঠিক সম্পর্কে চিন্তা করবেন না don'tMyError

try:
   do()
except MyError as my_err:
   pass # handle error 

প্রসঙ্গ পরিচালক

def do2():
   print('do2')
   with wrap_exceptions(MyError, IndexError):
       do()

ভিতরে do2, মধ্যে context manager, IndexErrorউত্থাপিত হয়, এটি মোড়ানো এবং উত্থাপিত করা হবেMyError


1
আসল ব্যতিক্রমটির জন্য "মোড়ানো" কী করবে দয়া করে তা ব্যাখ্যা করুন। আপনার কোডের উদ্দেশ্য কী এবং এটি কোন আচরণকে সক্ষম করে?
অ্যালেক্সিস

@ অ্যালেক্সিস - কয়েকটি উদাহরণ যুক্ত হয়েছে, আশা করি এটি সাহায্য করবে
হারুন_াব

-2

আপনার প্রয়োজনের সবচেয়ে সোজা সমাধানটি হ'ল:

try:
     upload(file_id)
except Exception as upload_error:
     error_msg = "Your upload failed! File: " + file_id
     raise RuntimeError(error_msg, upload_error)

এইভাবে আপনি পরে আপনার বার্তা এবং আপলোড ফাংশন দ্বারা নিক্ষেপ করা নির্দিষ্ট ত্রুটি মুদ্রণ করতে পারেন


1
এটি ধরা পরে তার ব্যতিক্রম বস্তুকে ছুঁড়ে ফেলে দেয় , তাই না, এটি প্রশ্নের প্রয়োজনীয়তা পূরণ করে না meet প্রশ্নটি জিজ্ঞাসা করে যে কীভাবে বিদ্যমান ব্যতিক্রমটি রাখা যায় এবং এতে থাকা সমস্ত দরকারী তথ্যের সাথে স্ট্যাকটি প্রচার চালিয়ে যাওয়ার জন্য একই ব্যতিক্রমটিকে কীভাবে অনুমতি দেওয়া যায়।
11
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.