Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's Python good practice for importing and offering optional features?

I'm writing a piece of software over on github. It's basically a tray icon with some extra features. I want to provide a working piece of code without actually having to make the user install what are essentially dependencies for optional features and I don't actually want to import things I'm not going to use so I thought code like this would be "good solution":

---- IN LOADING FUNCTION ---- features = []  for path in sys.path:        if os.path.exists(os.path.join(path, 'pynotify')):               features.append('pynotify')        if os.path.exists(os.path.join(path, 'gnomekeyring.so')):               features.append('gnome-keyring')  #user dialog to ask for stuff #notifications available, do you want them enabled? dlg = ConfigDialog(features)  if not dlg.get_notifications():     features.remove('pynotify')   service_start(features ...)  ---- SOMEWHERE ELSE ------  def service_start(features, other_config):          if 'pynotify' in features:                import pynotify                #use pynotify... 

There are some issues however. If a user formats his machine and installs the newest version of his OS and redeploys this application, features suddenly disappear without warning. The solution is to present this on the configuration window:

if 'pynotify' in features:     #gtk checkbox else:     #gtk label reading "Get pynotify and enjoy notification pop ups!" 

But if this is say, a mac, how do I know I'm not sending the user on a wild goose chase looking for a dependency they can never fill?

The second problem is the:

if os.path.exists(os.path.join(path, 'gnomekeyring.so')): 

issue. Can I be sure that the file is always called gnomekeyring.so across all the linux distros?

How do other people test these features? The problem with the basic

try:     import pynotify except:     pynotify = disabled 

is that the code is global, these might be littered around and even if the user doesn't want pynotify....it's loaded anyway.

So what do people think is the best way to solve this problem?

like image 389
Philluminati Avatar asked Feb 18 '09 21:02

Philluminati


People also ask

When should you use from import Python?

In Python, you use the import keyword to make code in one module available in another. Imports in Python are important for structuring your code effectively. Using imports properly will make you more productive, allowing you to reuse code while keeping your projects maintainable.

Should I use relative or absolute imports Python?

With your new skills, you can confidently import packages and modules from the Python standard library, third party packages, and your own local packages. Remember that you should generally opt for absolute imports over relative ones, unless the path is complex and would make the statement too long.


2 Answers

The try: method does not need to be global — it can be used in any scope and so modules can be "lazy-loaded" at runtime. For example:

def foo():     try:         import external_module     except ImportError:         external_module = None       if external_module:         external_module.some_whizzy_feature()     else:         print("You could be using a whizzy feature right now, if you had external_module.") 

When your script is run, no attempt will be made to load external_module. The first time foo() is called, external_module is (if available) loaded and inserted into the function's local scope. Subsequent calls to foo() reinsert external_module into its scope without needing to reload the module.

In general, it's best to let Python handle import logic — it's been doing it for a while. :-)

like image 75
Ben Blank Avatar answered Sep 23 '22 08:09

Ben Blank


You might want to have a look at the imp module, which basically does what you do manually above. So you can first look for a module with find_module() and then load it via load_module() or by simply importing it (after checking the config).

And btw, if using except: I always would add the specific exception to it (here ImportError) to not accidently catch unrelated errors.

like image 28
MrTopf Avatar answered Sep 25 '22 08:09

MrTopf