User-defined exception

[quote]
The import chain you have looks like this ASCII diagram:

X     fn3 -> fn4 -> fn5 -+
X             ^          |

[\quote]

Column of X’s added in an attempt to prevent Discuss from mangling the text further, just because I don’t trust the software.

My guess is that you had a row of dashes. Discuss truncates email posts just before a row of dashes (with or without whitespace) even if you guard it with code fences or an indent.

Here’s a test: the next few lines contain a row of hyphens, followed by a line of text. My prediction is that this post gets truncated at the comment below.

# The next line is a row of hyphens

[quote]
The import chain you have looks like this ASCII diagram:

X     fn3 -> fn4 -> fn5 -+
X             ^          |

[\quote]

Column of X’s added in an attempt to prevent Discuss from mangling the text further, just because I don’t trust the software.

Thank you.

My guess is that you had a row of dashes. Discuss truncates email posts just before a row of dashes (with or without whitespace) even if you guard it with code fences or an indent.

Looks like it is worse than that. I had a row of dashes with
leading/trailing plus signs.

Thanks for the heads up. I’ll repost with something different. And I’ll
flag this for filing as a bug with the Discourse folks. Not the first
time this has happenned to me, and presumably not for you either.

Thanks,
Cameron Simpson cs@cskk.id.au

@Pejamide before I was so rudely interrupted by the Discourse software,
I was saying:

What should one or more abovementioned programs to be corrected?

The import chain you have looks like this ASCII diagram:

fn3 -> fn4 -> fn5 -+
        ^          |

There was just a closed loop there. The followup text, discarded by
Discourse, was:

thus the import loop.

The usual fix is that the stuff you want should be in another “lower
level” module which defines fn4. Your current issue is that fn4
needs fn5 and fn5 needs fn4. You’d
fix this by taking the things they both need and moving them into some
common module eg “misc”. Example:

 misc.py
     class MyException(OtherException):
         ...
 fn4.py:
     from misc import MyException
     def fn4():
         ...
 fn5.py:
     from misc import MyException
     def fn5():
         ...
 fn3.py
     from fn4 import fn4
     from fn5 import fn5
     ... use fn4 and/or fn5 as required ...

Another approach would be to just merge fn4.py and fn5.py into a
single module so that they do not try to import each other. I’m
presuming there’s a reason that they are
separate though?

A third alternative is the “late import”. This is the less desirable
approach, because it’s a bit of a hack which cannot always be used - it
is usually better to structure your
modules in a bit of a hierachy as above. The late import:

 fn4.py:
     from fn5 import fn5
 fn5.py:
     def fn5():
         from fn4 import fn4, MyException
         ... use fn4 or MyException in here ..

This works because the inner import is not run unless the fn5()
function as actually called. By that stage both fn4 and fn5 have
been imported.

Usually we take a circular import to mean that your modules can be
better structures in a clean hierarchy instead of having two modules
codependent.

Occasionally that can’t be done, and a late import is the usual fix. But
try to structure your modules differently first.

Cheers,
Cameron Simpson cs@cskk.id.au

To my disappointment, your advice to use the following command doesn’t work either:

 MyException = M.MyException

I have reorganized my program as follows:

My function fn3 contains:

.......
from fnEXC import fnEXC 
fnEXC(.......)
.......
try:
    from fn4 import fn4
    fn4(.......)
except MyException as e:
    logging.error('MyException executing...')
    logging.error(e)
.......

My function ‘fnEXC ’ contains fully:

def fnEXC (.......):
    class MyException(PermissionError):
        import logging
        logging.error('MyException classed!')

My function ‘fn4’ contains:

.......
from fn5 import fn5
fn5(.......)
.......

My function ‘fn5’ contains:

.......
from fnEXC import MyException
MyException = fnEXC.MyException
raise MyException('123')
.......

I got the following error

<class 'ImportError'>: cannot import name 'MyException' from 'fnEXC ' (D:\Server/PythonNew/prd/pyx\fnEXC .py)
... called by File "D:\Server/PythonNew/tst/pyx\fn5.py", line 32, in fn5:   from fnEXC import MyException
... called by File "D:\Server/PythonNew/tst/pyx\fn4.py", line 33, in fn4:   fn5(glb)
... called by File "D:\Server\PythonNew\prd\pyx\fn3.py", line 60, in fn3:   fn4(glb)

Without the line ‘from fnEXC import MyException’ I got the following error:

Help, what should I have doing erroneous? What should the solution be in this difficult situation?

This code defines a function named fnEXC. It does not define an
exception class named MyException, because the function is not
called, and therefore the code inside it is not executed and anyway
it would define the exception class as a local variable, which would
vanish on return from the function.

Just put this in your fnEXC.py file:

 class MyException(PermissionError):
     import logging
     logging.error('MyException classed!')

and use:

 from fnEXC import MyException

Cheers,
Cameron Simpson cs@cskk.id.au

Unfortunately it does not work in my program, because the section ‘try + exception MyException’ must be processed in my function ‘fn3’. In this case the command ‘raise MyException’ in my function ‘fn5’ must then go to the command ‘exception MyException’ in my funxtion ‘fn3’.
Whta to do in this most important situation?

Unfortunately the command ‘raise MyException(‘123’)’ in the function ‘fn5’ does NOT jump to the command 'except MyException as e:
’ in the function ‘fn3’.
If it cannot be solved in this case, then it is NOT possible in a Python program, is it?

It’s unclear to me what you’re trying to achieve.

When an exception is raised, the current call stack is unwound looking
for an except clause which matches the exception. If one is found that
clause runs. If not, the unwind gets all the way out to the Python
system wide handler, which is what prints the stack trace etc.

If fn3 did not call fn5, then fn3 will not be involved in
handling the exception.

As I recall, you have had a few problems:

  • defining the MyException class, which I presume you’ve now
    accomplished
  • importing the MyException class name for use in a module, which I
    also presume you’ve now accomplished if you’re successfully raising
    the exception
  • handling the exception with a try/except when it occurs

Can you put the fn3() function, the fn5() function and the
MyException class all in a single file which reproduces your problem
when it runs?

Then show us that code and the output.

Thanks,
Cameron Simpson cs@cskk.id.au

Here you have the required information.
My function fnEXP contains:

def fnEXP():
    class MyException(PermissionError):
        import logging
        logging.error('MyException classified!')

My function fn3 contains:

from fnEXP import fnEXP 
fnEXP()
logging.error('MyException classified in fnEXP')
.......
try:
    from fn4 import fn4
    fn4(.......)
except MyException as e:
    logging.error('MyException executing...')
    logging.error(e)

My function ‘fn4’ contains:

.......
from fn5 import fn5
fn5(.......)
.......

My function ‘fn5’ contains:

.......
from fnEXP import fnEXP 
fnEXP()
logging.error('MyException classified in fn5')
.......
raise MyException('123')

I got the following error

'MyException classified!'
MyException classified in fn3
'MyException classified!'
MyException classified in fn5
<class 'NameError'>: name 'MyException ' is not defined
... called by File "D:\Server\PythonNew\prd\pyx\fn3.py", line 60, in fn3:   except MyException as e: 

What to do now?

MyException is defined inside fnEXP and therefore cannot be accessed outside of fnEXP. You need to either define MyException on the module level or return it from the function.

Please tell me how I can define MyException on the module. Please with some examples as I am new for those atters.
Ans please tell me how and what I have return of fnEXP with some examples.

Here you have the required information.
My function fnEXP contains:

You seem to be conflating functions with files. There is no need to have
these functions each in their own file. You’ll be a lot better off
having a single file defining your various functions unless there’s some
special reason to break things up.

With everything in a single file there’s no need to import names.

def fnEXP():
   class MyException(PermissionError):
       import logging
       logging.error('MyException classified!')

Alexander has already mentioned this, and it has also been mentioned
before. This function fnEXP defines a local class named
MyException. And then discards it (as any local variable is
descarded when you leave a function).

Also, that class is only defined when fnEXP is actaully called/run.
The file containing fnEXP only defines the fnEXP() function. It
does not call it.

Usually you’d define a class as a top level name. In your case, you
might make the contents of the fnEXP file just this:

 class MyException(PermissionError):
     import logging
     logging.error('MyException classified!')

Which would define MyException at the module level, immediately.
There’s no need for the fnEXP() function, as to my eye it does not not
anything else. So it can be discarded.

My function fn3 contains:

from fnEXP import fnEXP
fnEXP()

This is an odd way to do things (and ineffective, as you discover
later). It looks like you’re calling the fnEXP() function in order to
define the MyException class. But because that class is a local
variable
in the fnEXP() function, it is forgotten as soon as you
return from the function, and was never visible outside the function
anyway.

If you change the fnEXP file as suggested above, to just define the
MyException class at the top level, and drop the fnEXP() function
completely, you’ll end up with a module level MyException name.

And when you’ve done that you can go:

 from fnEXP import MyException

That line binds the module level name MyException in the fn3 file to
the same things to which is it bound in the fnEXP file. And that is
how the name MyException becomes usable in the fn3 file.

When you run it:

try:
   from fn4 import fn4
   fn4(.......)
except MyException as e:
   logging.error('MyException executing...')
   logging.error(e)

With your code, this line will raise the NameError you see because you
never imported the name MyException itself. If you change fnEXP to
define MyException as a top level name and then import that name to
the fn3 file, then the name MyException will be known and the
try/except will work.

Cheers,
Cameron Simpson cs@cskk.id.au

My program has been reorganized as follows:

My function fn3 contains:

def fn3():
  import logging
  logging.error('fn3: -001-')
  class MyException(PermissionError):
    import logging
    logging.error('MyException classified!')
  try:
    logging.error('fn3: -002-')
    from fn4 import fn4
    logging.error('fn3: -003-')
    fn4()
    logging.error('fn3: -004-')
  except MyException as e:
    logging.error('fn3: -005-')
    logging.error(e)
    logging.error('fn3: -006-')
  logging.error('fn3: -007-')

My function ‘fn4’ contains:

def fn4():
  import logging
  logging.error('fn4: -001-')
  from fn5 import fn5
  logging.error('fn4: -002-')
  fn5()
  logging.error('fn4: -003-')

My function ‘fn5’ contains:

def fn5():
  import logging
  logging.error('fn5: -001-')
  raise MyException('123')
  logging.error('fn5: -002-')

It gives errors:

fn3: -001-
MyException classified!
fn3: -002-```
fn3: -003-
fn4: -001-
fn4: -002-
fn5: -001-
<class 'NameError'>: name 'MyException' is not defined
... called by File "D:\Server\...\fn5.py", line 4, in fn5:   raise MyException('123')
... called by File "D:\Server\...\fn4.py", line 6, in fn4:   fn5()
... called by File "D:\Server\...\fn3.py", line 11, in fn3:   fn4()

Help, how to solve that difficult problem?

You’re still defining MyException as a local variable in the
function fn3(). That makes it unknown outside the function, hence
the NameError later.

Might I suggest:

  • define MyException outside the function as a module level name
  • import MyException in the files where it is used, so that they have
    that name available
  • import logging once at the top of each file - it is easier to read

So your fn3 file would look like this:

 import logging

 class MyException(PermissionError):
   logging.error('MyException classified!')

 def fn3():
   from fn4 import fn4
   logging.error('fn3: -001-')
   try:
     logging.error('fn3: -002-')
     logging.error('fn3: -003-')
     fn4()
     logging.error('fn3: -004-')
   except MyException as e:
     logging.error('fn3: -005-')
     logging.error(e)
     logging.error('fn3: -006-')
   logging.error('fn3: -007-')

and fn4 looks like this:

 import logging

 def fn4():
   from fn5 import fn5
   logging.error('fn4: -001-')
   logging.error('fn4: -002-')
   fn5()
   logging.error('fn4: -003-')

and fn5 looks like this:

 import logging

 def fn5():
   from fn3 import MyException
   logging.error('fn5: -001-')
   raise MyException('123')
   logging.error('fn5: -002-')

Normally we would also be be putting the imports of fn3, fn4, fn5
at the tops of files but you have a circular dependency (fn5 needs
fn3 needs fn4 needs fn5 …) so those imports need to be deferred.

The important thing here is to define MyException as a module level
nam in fn3, and to import MyException in the modules which use it.

Cheers,
Cameron Simpson cs@cskk.id.au

Yay! Thank you so much for your enlightening example, which finally made my program work perfectly! I do admire your terribly persistent patience for me.

Now I wonder, if you can answer my other problem “How use dynamic function name in import function?” in the same way. I usually learn a lot from examples. Hence this polite request.