8. Kesalahan *errors* dan Pengecualian *exceptions*
***************************************************

Sampai sekarang pesan kesalahan belum lebih dari yang disebutkan,
tetapi jika Anda telah mencoba contohnya, Anda mungkin telah melihat
beberapa. Ada (setidaknya) dua jenis kesalahan yang dapat dibedakan:
*syntax errors* dan *exceptions*.


8.1. Kesalahan Sintaksis
========================

Kesalahan sintaksis, juga dikenal sebagai kesalahan penguraian
*parsing*, mungkin merupakan jenis keluhan paling umum yang Anda
dapatkan saat Anda masih belajar Python:

   >>> while True print 'Hello world'
     File "<stdin>", line 1
       while True print 'Hello world'
                      ^
   SyntaxError: invalid syntax

The parser repeats the offending line and displays a little 'arrow'
pointing at the earliest point in the line where the error was
detected.  The error is caused by (or at least detected at) the token
*preceding* the arrow: in the example, the error is detected at the
keyword "print", since a colon ("':'") is missing before it.  File
name and line number are printed so you know where to look in case the
input came from a script.


8.2. Pengecualian
=================

Bahkan jika suatu pernyataan atau ungkapan secara sintaksis benar, itu
dapat menyebabkan kesalahan ketika suatu usaha dilakukan untuk
mengeksekusinya. Kesalahan yang terdeteksi selama eksekusi disebut
*exceptions* dan tidak fatal tanpa syarat: Anda akan segera belajar
cara menanganinya dalam program Python. Namun, sebagian besar
pengecualian tidak ditangani oleh program, dan menghasilkan pesan
kesalahan seperti yang ditunjukkan di sini:

   >>> 10 * (1/0)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   ZeroDivisionError: integer division or modulo by zero
   >>> 4 + spam*3
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   NameError: name 'spam' is not defined
   >>> '2' + 2
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: cannot concatenate 'str' and 'int' objects

Baris terakhir dari pesan kesalahan menunjukkan apa yang terjadi.
Pengecualian ada berbagai jenis yang berbeda, dan tipe dicetak sebagai
bagian dari pesan: tipe dalam contoh adalah "ZeroDivisionError",
"NameError" dan "TypeError". String yang dicetak sebagai jenis
pengecualian adalah nama pengecualian bawaan yang terjadi. Ini berlaku
untuk semua pengecualian bawaan, tetapi tidak harus sama untuk
pengecualian yang dibuat pengguna (meskipun ini adalah konvensi yang
bermanfaat). Nama pengecualian standar adalah pengidentifikasi bawaan
(bukan kata kunci yang dipesan *reserved keyword*).

Sisa baris menyediakan detail berdasarkan jenis pengecualian dan apa
yang menyebabkannya.

Bagian sebelumnya dari pesan kesalahan menunjukkan konteks di mana
pengecualian terjadi, dalam bentuk *stack traceback*. Secara umum
berisi daftar baris sumber *stack traceback*; namun, ini tidak akan
menampilkan baris yang dibaca dari masukan standar.

Built-in Exceptions memberikan daftar pengecualian bawaan dan artinya.


8.3. Menangani Pengecualian
===========================

Dimungkinkan untuk menulis program yang menangani pengecualian yang
dipilih. Lihatlah contoh berikut, yang meminta masukan dari pengguna
sampai integer yang valid telah dimasukkan, tetapi memungkinkan
pengguna untuk menghentikan program (menggunakan "Control-C" atau apa
pun yang didukung sistem operasi); perhatikan bahwa gangguan yang
dibuat pengguna ditandai dengan munculnya pengecualian
"KeyboardInterrupt".

   >>> while True:
   ...     try:
   ...         x = int(raw_input("Please enter a number: "))
   ...         break
   ...     except ValueError:
   ...         print "Oops!  That was no valid number.  Try again..."
   ...

Pernyataan "try" berfungsi sebagai berikut.

* Pertama, *try clause* (pernyataan(-pernyataan) di antara kata
  kunci "try" dan "except") dieksekusi.

* Jika tidak ada pengecualian terjadi, *except clause* dilewati dan
  eksekusi pernyataan :keyword: *try* selesai.

* Jika pengecualian terjadi selama eksekusi klausa *try*, sisa
  klausa dilewati. Kemudian jika jenisnya cocok dengan pengecualian
  yang dinamai dengan kata kunci "exception", klausa *except*
  dioperasikan, dan kemudian eksekusi berlanjut setelah pernyataan
  "try".

* Jika terjadi pengecualian yang tidak cocok dengan pengecualian
  yang disebutkan dalam klausa kecuali, itu diteruskan ke luar
  pernyataan "try"; jika tidak ada penangan yang ditemukan, ini adalah
  *unhandled exception* dan eksekusi berhenti dengan pesan seperti
  yang ditunjukkan di atas.

A "try" statement may have more than one except clause, to specify
handlers for different exceptions.  At most one handler will be
executed. Handlers only handle exceptions that occur in the
corresponding try clause, not in other handlers of the same "try"
statement.  An except clause may name multiple exceptions as a
parenthesized tuple, for example:

   ... except (RuntimeError, TypeError, NameError):
   ...     pass

Note that the parentheses around this tuple are required, because
"except ValueError, e:" was the syntax used for what is normally
written as "except ValueError as e:" in modern Python (described
below). The old syntax is still supported for backwards compatibility.
This means "except RuntimeError, TypeError" is not equivalent to
"except (RuntimeError, TypeError):" but to "except RuntimeError as
TypeError:" which is not what you want.

Klausa *except* terakhir dapat menghilangkan nama-(nama) pengecualian,
untuk berfungsi sebagai *wildcard*. Gunakan ini dengan sangat hati-
hati, karena mudah untuk menutupi kesalahan nyata pemrograman dengan
cara ini! Ini juga dapat digunakan untuk mencetak pesan kesalahan dan
kemudian menimbulkan kembali pengecualian (memungkinkan pemanggil
untuk menangani pengecualian juga)

   import sys

   try:
       f = open('myfile.txt')
       s = f.readline()
       i = int(s.strip())
   except IOError as e:
       print "I/O error({0}): {1}".format(e.errno, e.strerror)
   except ValueError:
       print "Could not convert data to an integer."
   except:
       print "Unexpected error:", sys.exc_info()[0]
       raise

Pernyataan "try" ... >>:keywird:`except`<< memiliki opsi *else
clause*, yang, jika ada, harus mengikuti semua klausa *except*. Ini
berguna untuk kode yang harus dijalankan jika klausa *try* tidak
menimbulkan pengecualian. Sebagai contoh:

   for arg in sys.argv[1:]:
       try:
           f = open(arg, 'r')
       except IOError:
           print 'cannot open', arg
       else:
           print arg, 'has', len(f.readlines()), 'lines'
           f.close()

The use of the "else" clause is better than adding additional code to
the "try" clause because it avoids accidentally catching an exception
that wasn't raised by the code being protected by the "try" ...
"except" statement.

Ketika pengecualian terjadi, itu mungkin memiliki nilai terkait, juga
dikenal sebagai *argument* pengecualian. Kehadiran dan jenis argumen
tergantung pada jenis pengecualian.

The except clause may specify a variable after the exception name (or
tuple). The variable is bound to an exception instance with the
arguments stored in "instance.args".  For convenience, the exception
instance defines "__str__()" so the arguments can be printed directly
without having to reference ".args".

One may also instantiate an exception first before raising it and add
any attributes to it as desired.

   >>> try:
   ...     raise Exception('spam', 'eggs')
   ... except Exception as inst:
   ...     print type(inst)     # the exception instance
   ...     print inst.args      # arguments stored in .args
   ...     print inst           # __str__ allows args to be printed directly
   ...     x, y = inst.args
   ...     print 'x =', x
   ...     print 'y =', y
   ...
   <type 'exceptions.Exception'>
   ('spam', 'eggs')
   ('spam', 'eggs')
   x = spam
   y = eggs

If an exception has an argument, it is printed as the last part
('detail') of the message for unhandled exceptions.

Penangan pengecualian tidak hanya menangani pengecualian jika mereka
muncul segera di klausa *try*, tetapi juga jika mereka terjadi di
dalam fungsi yang disebut (bahkan secara tidak langsung) di klausa
*try*. Sebagai contoh:

   >>> def this_fails():
   ...     x = 1/0
   ...
   >>> try:
   ...     this_fails()
   ... except ZeroDivisionError as detail:
   ...     print 'Handling run-time error:', detail
   ...
   Handling run-time error: integer division or modulo by zero


8.4. Memunculkan Pengecualian
=============================

Pernyataan "raise" memungkinkan programmer untuk memaksa pengecualian
yang ditentukan terjadi. Sebagai contoh:

   >>> raise NameError('HiThere')
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   NameError: HiThere

The sole argument to "raise" indicates the exception to be raised.
This must be either an exception instance or an exception class (a
class that derives from "Exception").

Jika Anda perlu menentukan apakah pengecualian muncul tetapi tidak
bermaksud menanganinya, bentuk yang lebih sederhana dari pernyataan
"raise" memungkinkan Anda untuk memunculkan kembali pengecualian:

   >>> try:
   ...     raise NameError('HiThere')
   ... except NameError:
   ...     print 'An exception flew by!'
   ...     raise
   ...
   An exception flew by!
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
   NameError: HiThere


8.5. Pengecualian yang Ditentukan Pengguna
==========================================

Programs may name their own exceptions by creating a new exception
class (see Kelas-kelas for more about Python classes).  Exceptions
should typically be derived from the "Exception" class, either
directly or indirectly.  For example:

   >>> class MyError(Exception):
   ...     def __init__(self, value):
   ...         self.value = value
   ...     def __str__(self):
   ...         return repr(self.value)
   ...
   >>> try:
   ...     raise MyError(2*2)
   ... except MyError as e:
   ...     print 'My exception occurred, value:', e.value
   ...
   My exception occurred, value: 4
   >>> raise MyError('oops!')
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   __main__.MyError: 'oops!'

In this example, the default "__init__()" of "Exception" has been
overridden.  The new behavior simply creates the *value* attribute.
This replaces the default behavior of creating the *args* attribute.

Kelas pengecualian dapat didefinisikan yang melakukan apa saja yang
dapat dilakukan oleh kelas lain, tetapi biasanya tetap sederhana,
seringkali hanya menawarkan sejumlah atribut yang memungkinkan
informasi tentang kesalahan diekstraksi oleh penangan sebagai
pengecualian. Saat membuat modul yang dapat menimbulkan beberapa
kesalahan berbeda, praktik yang umum adalah membuat kelas dasar untuk
pengecualian yang ditentukan oleh modul itu, dan mensubkelaskan kelas
itu untuk membuat kelas pengecualian khusus untuk kondisi kesalahan
yang berbeda:

   class Error(Exception):
       """Base class for exceptions in this module."""
       pass

   class InputError(Error):
       """Exception raised for errors in the input.

       Attributes:
           expr -- input expression in which the error occurred
           msg  -- explanation of the error
       """

       def __init__(self, expr, msg):
           self.expr = expr
           self.msg = msg

   class TransitionError(Error):
       """Raised when an operation attempts a state transition that's not
       allowed.

       Attributes:
           prev -- state at beginning of transition
           next -- attempted new state
           msg  -- explanation of why the specific transition is not allowed
       """

       def __init__(self, prev, next, msg):
           self.prev = prev
           self.next = next
           self.msg = msg

Sebagian besar pengecualian didefinisikan dengan nama yang diakhiri
dengan "Error", mirip dengan penamaan pengecualian standar.

Banyak modul standar menentukan pengecualian mereka sendiri untuk
melaporkan kesalahan yang mungkin terjadi pada fungsi yang mereka
tetapkan. Informasi lebih lanjut tentang kelas disajikan dalam bab
*tut-class*.


8.6. Mendefinisikan Tindakan Pembersihan
========================================

Pernyataan "try" memiliki klausa opsional lain yang dimaksudkan untuk
menentukan tindakan pembersihan yang harus dijalankan dalam semua
keadaan. Sebagai contoh:

   >>> try:
   ...     raise KeyboardInterrupt
   ... finally:
   ...     print 'Goodbye, world!'
   ...
   Goodbye, world!
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
   KeyboardInterrupt

A *finally clause* is always executed before leaving the "try"
statement, whether an exception has occurred or not. When an exception
has occurred in the "try" clause and has not been handled by an
"except" clause (or it has occurred in an "except" or "else" clause),
it is re-raised after the "finally" clause has been executed.  The
"finally" clause is also executed "on the way out" when any other
clause of the "try" statement is left via a "break", "continue" or
"return" statement.  A more complicated example (having "except" and
"finally" clauses in the same "try" statement works as of Python 2.5):

   >>> def divide(x, y):
   ...     try:
   ...         result = x / y
   ...     except ZeroDivisionError:
   ...         print "division by zero!"
   ...     else:
   ...         print "result is", result
   ...     finally:
   ...         print "executing finally clause"
   ...
   >>> divide(2, 1)
   result is 2
   executing finally clause
   >>> divide(2, 0)
   division by zero!
   executing finally clause
   >>> divide("2", "1")
   executing finally clause
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
     File "<stdin>", line 3, in divide
   TypeError: unsupported operand type(s) for /: 'str' and 'str'

As you can see, the "finally" clause is executed in any event.  The
"TypeError" raised by dividing two strings is not handled by the
"except" clause and therefore re-raised after the "finally" clause has
been executed.

Dalam aplikasi dunia nyata, klausa "finally" berguna untuk melepaskan
sumber daya eksternal (seperti berkas atau koneksi jaringan), terlepas
dari apakah penggunaan sumber daya tersebut berhasil.


8.7. Tindakan Pembersihan yang Sudah Ditentukan
===============================================

Beberapa objek mendefinisikan tindakan pembersihan standar yang harus
dilakukan ketika objek tidak lagi diperlukan, terlepas dari apakah
operasi menggunakan objek berhasil atau gagal. Lihatlah contoh
berikut, yang mencoba membuka berkas dan mencetak isinya ke layar.

   for line in open("myfile.txt"):
       print line,

The problem with this code is that it leaves the file open for an
indeterminate amount of time after the code has finished executing.
This is not an issue in simple scripts, but can be a problem for
larger applications. The "with" statement allows objects like files to
be used in a way that ensures they are always cleaned up promptly and
correctly.

   with open("myfile.txt") as f:
       for line in f:
           print line,

After the statement is executed, the file *f* is always closed, even
if a problem was encountered while processing the lines. Other objects
which provide predefined clean-up actions will indicate this in their
documentation.
