Stop importing a module and return to main

hi… first time here… as i could find any help on this, i hope here i would find it

i came from php… there i make a heavy use of include_once … since python execfile() has been deprecated i created my own solution using with, open, compile and exec

i want to do things more pythonic but i found import very compicated with its scopes and namespaces etc. compared with php include_once … for example in php i can make things like this:

/**** index.php ****/
function die($msg){
  exit("<html><script>alert('$msg');</script></html>");
}

include_once 'db.php';

// more code

/**** db.php ****/
if !( $db = mysqli_connect($myhost, $myport, $myuser, $mypass) ){
  die('database connection error');
}

// more database functions

but in python it can’t do easily (calling a parent function from included file)… so i found this solution: python - Call function from parent module in imported module - Stack Overflow

""" index.py """
def die(msg):
  print(f'<some format> {msg}')
  exit()

import db

if __name__ == '__main__':
  # more code

""" db.py """
import index

if not ( db := pysqli_connect(myhost, myport, myuser, mypass) ): # hypothetic function
  index.die('database connection error');

# more database functions

i know this could be a circular import but python skips second imports and you have this code to prevent it further: if __name__ == '__main__':

my question is… supposing my # more code has 1000+ lines… is there a way to do the opposite?, stop and return to parent module in importing phase like this??

if __name__ != '__main__':
  stop_import_and_return_to_parent

# more code with 1000+ lines

i tried with return, break, and continue but all these exits and raises some like SyntaxError: 'keyword' outside loop/function… note this is not a question about break/return loops/scopes execution in runtime but interrupt module importing in the middle earlier in importing phase (mostly to avoid large sub-scopes and indentations and keep code clear)

thanks in advance for your responses

The simple answer is to stop writing PHP and start writing Python. Below we go into that.

Do not write “utility functions” in your main program

Why does your die function need to be in index.py? Put it in some other source file and import it wherever you need it. Don’t worry about the time it takes to import it, after it has been imported once, Python will only link to it, not load it again.

Don’t be afraid of import

import loads and executes a module as a script. That means that any classes or functions are created for use in the same or other modules. Any statements that are not in a class or function are executed, e.g. imports that your module needs. The execution will only be done once, as said before on subsequent imports only links will be made to the already loaded module/classes/functions/instances.

No, and you should not need it. If you have code you do not want execute on import, put it in a function. After import it will be ready to be executed, waiting for your program to call it. Only put code in statements outside functions if it needs to be executed at import.

Put import at the top of your source

It is pythonic to put all your imports at the top of your module. There are situations where you do not want to do that, but these are rare.

If you find an error, raise an exception

Python uses exceptions for indicating errors in your program. Your die function obscures things that the interpreter will give you, e.g. the traceback, that you will need to find out what went wrong and where it went wrong. Python has a large amount of exceptions which come with it. If you want another one, you can just create it by subclassing BaseException or any other exception of your liking.

Other programmers, including yourself in 2 months, will be wondering why you created something as your die function.

hi, thanks for your answer… so as i see i think you agree with me that import are more complicated than execfile() (or even php include_once)

“Do not write “utility functions” in your main program”

do you mean to make a “common functions” module and import it in all needed files, like this?

""" main.py """
import utils
utils.nicePrint('welcome')
import core
#blah

""" core.py """
import utils
utils.nicePrint('initializing...')
#blah

""" utils.py """
def nicePrint(msg):
  print(f'-=<o0| {msg} |0o>=-')

"Don’t be afraid of import"

i am not afraid but i feel it tedious… to repeat the work in many places and write long names, and being careful to not mess something, it gets tangled… i am not afraid to mess something cause anything can be repaired, just i don’t feel it easy… i feel this is related with above paragraph

“Put import at the top of your source”

i agree that putting all imports (or includes, or #defines) at top is more clean, readable and maintainable… but i do it this way in the sake of process efficiency… consider to make quick checks before to load huge amounts of imports and codes

import sys # to do quick checks before load the rest of the program

if len(sys.argv) < 2:
  print(f"info: i need a subcommand, type '{sys.argv[0]} -h' for help")
  exit()

if sys.argv[1] == '-h':
  print(f"info: usage: '{{sys.argv[0]} -h | -v | -c [confFile]'")
  exit()

if sys.argv[1] == '-v':
  print('info: version 5.2.0')
  exit()

if sys.argv[1] != '-c':
  print(f"info: unknown subcommand, type '{sys.argv[0]} -h' for help")
  exit()

# did you see i wrote this many times?
#   print('info: <something>')
#   exit()

confFile = sys.argv[2]  if len(sys.argv) > 2 else 'default.conf'

# now load the rest, so you don't have to load the *entire* program just to do
# some tiny checks, realize some failed and have to drop everything

import threading, socket, time, pyserial, tkinter, datetime, re
import pathlib, urllib, sqlite3, zipfile, bzip, csv, base64
import my_awesome_lib1, my_awesome_lib2, my_awesome_lib3
# import etc etc etc

# huge codes
# huge codes
# huge codes

“If you find an error, raise an exception”

i think i know exceptions more or less… i also coded some things in java (mostly minecraft plugins, check my gitlab) and sometimes i had to work with exceptions heavily, even create my own … but the die() function was more to avoid duplicated code as you had seen in the example above, like the php exit($msg) that is very practical

anyway…

i want to thank you for taking your time to kindly answer me this very nice and complete reply… after reading maybe you agree with me that import is some complicated… actually i currently don’t see any practical advantage (security?) compared with this… if anybody could tell me some i will feel very appreciated?

with open(includedFilePath) as included:
  exec(compile(included, includedFilePath, 'exec'))

Sorry, it took some time to get back to you.

I would not want to be the judge of that; I never programmed in PHP, nor did I use execfile (I used Python 3 from starting with Python). I read the documentation of include_once, it looks a lot like import.

That is one possibility. Generally I prefer to create modules that have “meaning” for either the domain or the program structure. In this case I could imagine creating an initializeapplication module, but utils is probably well understood also.

Now to your code:

# did you see i wrote this many times?
#   print('info: <something>')
#   exit()

Yes. I do not mind that, code is read more than it is written and this way the code is very clear. If you want a solution for this issue, continue reading to the bottom.

What I do take issue with, is the checking of the arguments in-line. In this source, the “general structure” of your program should show up, not clouded by this kind of detail.

I created 2 files. This is yourapp.py::

import sys # to do quick checks before load the rest of the program
import checkargs

# did you see i wrote this many times?
#   print('info: <something>')
#   exit()

# now load the rest, so you don't have to load the *entire* program just to do
# some tiny checks, realize some failed and have to drop everything

confFile = sys.argv[2]  if len(sys.argv) > 2 else 'default.conf'

print(f"Now initialize the application with configuration {confFile} ")

And the file checkargs.py:

import sys # to do quick checks before load the rest of the program

if len(sys.argv) < 2:
  print(f"info: i need a subcommand, type '{sys.argv[0]} -h' for help")
  exit()

if sys.argv[1] == '-h':
  print(f"info: usage: '{sys.argv[0]} -h | -v | -c [confFile]'")
  exit()

if sys.argv[1] == '-v':
  print('info: version 5.2.0')
  exit()

if sys.argv[1] != '-c':
  print(f"info: unknown subcommand, type '{sys.argv[0]} -h' for help")
  exit()

This works and is as simple as your solution.

This came from the conversion from Python2 to Python3 where execfile was removed. This made it possible to simply convert that code, without having to worry about namespace pollution. You should only use that in case you want to do such conversion, import is simpler, and at the same time more flexible.

Now for the “improved” checkargs.py.

from sys import argv # to do quick checks before load the rest of the program

if len(argv) < 2 or argv[1] != '-c':
    if len(argv) < 2:
        print(f"info: i need a subcommand, type '{argv[0]} -h' for help")
    elif argv[1] == '-h':
        print(f"info: usage: '{argv[0]} -h | -v | -c [confFile]'")
    elif argv[1] == '-v':
        print('info: version 5.2.0')
    else:
        print(f"info: unknown subcommand, type '{argv[0]} -h' for help")
    exit()

Only 1 exit(), code that is about as clear as the original. The cyclomatic complexity is only slightly higher.

thanks for your clarifications… i think i will spend some time to accustom myself to this new way of modular programing

anyway i am not a developer but a sysadmin, i am totally autodidact on programming and sometimes it costs me a little